You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by ha...@apache.org on 2017/03/16 13:37:36 UTC

[16/42] flex-asjs git commit: And here’s TLF…

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/fd08d137/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/elements/ListElement.as
----------------------------------------------------------------------
diff --git a/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/elements/ListElement.as b/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/elements/ListElement.as
new file mode 100644
index 0000000..76acbb8
--- /dev/null
+++ b/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/elements/ListElement.as
@@ -0,0 +1,645 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package org.apache.flex.textLayout.elements
+{
+	import org.apache.flex.textLayout.debug.assert;
+	import org.apache.flex.textLayout.events.ModelChange;
+	import org.apache.flex.textLayout.formats.BlockProgression;
+	import org.apache.flex.textLayout.formats.Direction;
+	import org.apache.flex.textLayout.formats.FormatValue;
+	import org.apache.flex.textLayout.formats.IListMarkerFormat;
+	import org.apache.flex.textLayout.formats.ListStyleType;
+	import org.apache.flex.textLayout.formats.Suffix;
+
+	
+
+	/** 
+	 * The List class is used for grouping together items into a numbered or unnumbered list. A ListElement's children may be of type ListItemElement,
+	 * ListElement, ParagraphElement, or DivElement. 
+	 * 
+	 * <p>Each ListElement creates a scope with an implicit counter 'ordered'.</p>
+	 * 
+	 * @see org.apache.flex.textLayout.formats.ITextLayoutFormat#listStyleType
+	 * @see org.apache.flex.textLayout.formats.ITextLayoutFormat#listStylePosition
+	 * @see org.apache.flex.textLayout.formats.ListMarkerFormat
+	 * 
+	 * @playerversion Flash 10
+	 * @playerversion AIR 1.5
+	 * @langversion 3.0
+	 *
+	 */
+	public class ListElement extends ContainerFormattedElement implements IListElement
+	{
+		/** 
+		 * Specifies the name of the ListMarker format
+		 * @private
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		static public const LIST_MARKER_FORMAT_NAME:String = "listMarkerFormat";
+		
+		override public function get className():String{
+			return "ListElement";
+		}
+		/** @private */
+		override protected function get abstract():Boolean
+		{ return false; }
+		
+		/** @private */
+		public override function get defaultTypeName():String
+		{ return "list"; }
+		
+		/** @private */
+		public override function canOwnFlowElement(elem:IFlowElement):Boolean
+		{
+			return !(elem is ITextFlow) && !(elem is IFlowLeafElement) && !(elem is ISubParagraphGroupElementBase);
+		}
+		
+		/** @private if its in a numbered list expand the damage to all list items - causes the numbers to be regenerated */
+		public override function modelChanged(changeType:String, elem:IFlowElement, changeStart:int, changeLen:int, needNormalize:Boolean = true, bumpGeneration:Boolean = true):void
+		{
+			// this is a bit aggressive - could damage with a new damage type (LIST_MARKER_DAMAGE) if we wanted to really optimize list item addition/removal
+			if ((changeType == ModelChange.ELEMENT_ADDED || changeType == ModelChange.ELEMENT_REMOVAL) && (elem is ListItemElement) && this.isNumberedList())
+			{
+				// for list renumbering
+				changeStart = elem.parentRelativeStart;
+				changeLen = textLength-elem.parentRelativeStart;
+			}
+			super.modelChanged(changeType,elem,changeStart,changeLen,needNormalize,bumpGeneration);
+		}
+		
+		/** @private */
+		public override function getEffectivePaddingLeft():Number
+		{ 
+			if (computedFormat.paddingLeft != FormatValue.AUTO)
+				return computedFormat.paddingLeft;
+			
+			var tf:ITextFlow = getTextFlow();
+			if (!tf || tf.computedFormat.blockProgression != BlockProgression.TB || computedFormat.direction != Direction.LTR)
+				return 0;
+			
+			return computedFormat.listAutoPadding;
+		}
+		
+		/** @private */
+		public override function getEffectivePaddingRight():Number
+		{ 
+			if (computedFormat.paddingRight != FormatValue.AUTO)
+				return computedFormat.paddingRight;
+			
+			var tf:ITextFlow = getTextFlow();
+			if (!tf || tf.computedFormat.blockProgression != BlockProgression.TB || computedFormat.direction != Direction.RTL)
+				return 0;
+			
+			return computedFormat.listAutoPadding;
+		}
+		/** @private */
+		public override function getEffectivePaddingTop():Number
+		{ 
+			if (computedFormat.paddingTop != FormatValue.AUTO)
+				return computedFormat.paddingTop;
+			
+			var tf:ITextFlow = getTextFlow();
+			if (!tf || tf.computedFormat.blockProgression != BlockProgression.RL || computedFormat.direction != Direction.LTR)
+				return 0;
+			
+			return computedFormat.listAutoPadding;
+		}
+		
+		/** @private */
+		public override function getEffectivePaddingBottom():Number
+		{ 
+			if (computedFormat.paddingBottom != FormatValue.AUTO)
+				return computedFormat.paddingBottom;
+			
+			var tf:ITextFlow = getTextFlow();
+			if (!tf || tf.computedFormat.blockProgression != BlockProgression.RL || computedFormat.direction != Direction.RTL)
+				return 0;
+			
+			return computedFormat.listAutoPadding;
+		}
+
+		/** avoid switches and if-else statements.  switches behave like if-else
+		 * http://www.w3.org/Style/CSS/Test/CSS1/current/sec563.htm
+		 * http://en.wikipedia.org/wiki/Unicode_Geometric_Shapes
+		 * @private
+		 */
+		public static const constantListStyles:Object = {
+			none:	"",
+			disc:	"\u2022",	// black bullet
+			circle:	"\u25e6",	// white bullet
+			square:	"\u25a0",	// black square
+			box:	"\u25a1",	// white square
+			check:	"\u2713",	// check mark
+			diamond:"\u25c6",	// black diamond
+			hyphen:	"\u2043"	// hyphen bullet
+		};
+
+		private static const romanDigitFunction:Vector.<Function> = Vector.<Function>([
+			function (o:String,f:String,t:String):String { return ""; },
+			function (o:String,f:String,t:String):String { return o; },
+			function (o:String,f:String,t:String):String { return o+o; },
+			function (o:String,f:String,t:String):String { return o+o+o; },
+			function (o:String,f:String,t:String):String { return o+f; },
+			function (o:String,f:String,t:String):String { return f; },
+			function (o:String,f:String,t:String):String { return f+o; },
+			function (o:String,f:String,t:String):String { return f+o+o; },
+			function (o:String,f:String,t:String):String { return f+o+o+o; },
+			function (o:String,f:String,t:String):String { return o+t; }
+		]);
+		
+		/** @private roman string support - doesn't follow the CSS spec for numbers > 4000.  The lines above aren't that interesting. In fact doesn't follow CSS spec at all. */
+		public static function createRomanString(n:int,data:Vector.<String>):String
+		{
+			var leading:String = "";
+			
+			// there are other ways to handle values > 4000 - this works
+			while (n >= 1000)
+			{
+				leading += data[6];
+				n -= 1000;
+			}
+			
+			// avoid switches and loops - add in the suffix
+			return leading + romanDigitFunction[int(n / 100)](data[4],data[5],data[6])
+			     + romanDigitFunction[int((n/10) % 10)](data[2],data[3],data[4])
+			     + romanDigitFunction[int(n % 10)](data[0],data[1],data[2]);
+		}
+		
+		private static const upperRomanData:Vector.<String> = Vector.<String>(['I', 'V', 'X', 'L', 'C', 'D', 'M']);
+		/** @private 
+		 * From http://www.w3.org/TR/css3-lists/#list-content
+		 * upper-roman
+		    Numbers are converted to roman numerals using the steps described below. The phrase "take the three least significant numbers" means divide by 1000, truncate (drop the fractional part), multiply by one thousand, and subtract this from the original number. So the three least significant figures of 1004 is 4.
+		
+		       1. If the number is in the range 0 to 1000 inclusive, use the following steps:
+		             1. If the number is 1000, add \u216f U+216F and skip to the end.
+		             2. If the number is 900 or more, add \u216d\u216f U+216D U+216F and subtract 900.
+		             3. If the number is 500 or more, add \u216e U+216E and subtract 500.
+		             4. If the number is 400 or more, add \u216d\u216e U+216D U+216E and subtract 400.
+		             5. While the number is greater than or equal to 100, add \u216d U+216D and subtract 100.
+		             6. If the number is 90 or more, add \u2169\u216d U+2169 U+216D and subtract 90.
+		             7. If the number is 50 or more, add \u216c U+216C and subtract 50.
+		             8. If the number is 40 or more, add \u2169\u216c U+2169 U+216C and subtract 40.
+		             9. While the number is greater than 12, add \u2169 U+2169 and subtract 10.
+		            10. If the number is greater than 0, then add the appropriate roman numeral from \u2160 U+2160 (1) to \u216b U+216B (12). For example, if the number is 8, add \u2167 U+2167. 
+		       2. If the number is in the range 1000 to 40000 exclusive, then take the three least significant numbers and convert them using step 1. Then convert the remainder using the following steps and prepend it to the result.
+		             1. While the number is greater than or equal to 10000, add \u2182 U+2182 and subtract 10000.
+		             2. If the number is 9000, add \u216f\u2182 U+216F U+2182 and skip to the end.
+		             3. If the number is 4000, add \u216f\u2181 U+216F U+2181 and skip to the end.
+		             4. If the number is 5000 or more, add \u2181 U+2181 and subtract 5000.
+		             5. While the number is greater than 0, add \u216f U+216F and subtract 1000. 
+		       3. If the number is greater than 40000, then take the three least significant figures and convert them using step 1, calling that the result. Then convert the remainder by dividing it by 1000, passing it through the entire algorithm starting at the top, adding an overbar to it, and prepending it to the current result. For very large numbers, this step may be entered several times, resulting in the first few characters of the result having multiple overbars. 
+		
+		    The suffix for roman numerals is a dot . U+002E.
+		
+		    Zero and negative numbers are rendered using the decimal numbering style. 
+			 
+			Modified as follows:
+			 * The letterspacing behavior of <U+2169 \u2169 ROMAN NUMERAL TEN, U+2161 \u2161 ROMAN NUMERAL TWO> is probably not what we want
+			 * while that of <X, I, I> is the one you want.
+				(Of course, keep using U+2180 \u2180 ROMAN NUMERAL ONE THOUSAND C D and similar.)
+	 	*/
+		public static function upperRomanString(n:int):String
+		{ 
+			if (n <= 0)
+				return decimalString(n);
+			
+			// normal case - just do it and be done
+			if (n <= 1000)
+				return createRomanString(n,upperRomanData);
+				
+			// not to spec but overbars are too complicated for a non-intersting case with TLF 2.0 numbering
+			if (n >= 40000)
+				return decimalString(n);
+			
+			// the more complex case defined as bove
+			var lowerString:String = createRomanString(n%1000,upperRomanData);
+			var highString:String = "";
+			
+			n -= n%1000;
+			
+			while (n >= 10000)
+			{
+				highString += String.fromCharCode(0x2182);
+				n -= 10000;
+			}
+			if (n == 9000)
+				highString += "M" + String.fromCharCode(0x2182);
+			else if (n == 4000)
+				highString += "M" + String.fromCharCode(0x2181);
+			else
+			{
+				if (n >= 5000)
+				{
+					highString += String.fromCharCode(0x2181);
+					n -= 5000;
+				}
+				while (n > 0)
+				{
+					highString += "M";
+					n -= 1000;
+				}
+			}
+
+			return highString+lowerString; 
+		}	// INCORRECT
+		
+		private static const lowerRomanData:Vector.<String> = Vector.<String>(['i', 'v', 'x', 'l', 'c', 'd', 'm']);
+		/** @private 
+		 * The algorithm for this numbering style is identical to the upper-roman  style but using \u2170-\u217f U+2170-U+217F instead of \u2160-\u216f U+2160-U+216F, 
+		 * and ignoring step 2 (i.e. not treating numbers 1000-40000 in a special way, since there are no lowercase equivalents to \u2181 U+2181 and \u2182 U+2182). 
+		 * Numbers up to 3999 convert and over 4000 are going to go to decimal.
+		 */
+		public static function lowerRomanString(n:int):String
+		{ return n> 0 && n < 4000 ? createRomanString(n,lowerRomanData) : decimalString(n); }
+		
+		/** @private */
+		public static function decimalString(n:int):String
+		{ return n.toString(); }
+		/** @private */
+		public static function decimalLeadingZeroString(n:int):String
+		{ return n <= 9 && n >= -9 ? "0"+n.toString() : n.toString(); }
+		
+		
+		// Supports "Numeric" style base 10 lists 
+		// http://www.w3.org/TR/css3-lists/#numeric
+		// NOTE: easy to pass in a base if other than base 10 is needed
+		// NOTE: easy to add non-positive number support 
+		// (from above link: "For all these systems, negative numbers are first converted as if they were positive numbers, then have - U+002D prefixed.")
+		// n is the number.  zero is the charChode of zero and is used as an offset
+		// see http://www.w3.org/TR/css3-lists/#list-content
+		/** @private */
+		public static function createNumericBaseTenString(n:int,zero:int):String
+		{
+			CONFIG::debug { assert(n >= 0,"Bad number passed to createBaseTenString"); }
+			
+			if (n == 0)
+				return String.fromCharCode(zero);
+			
+			var rslt:String = "";
+			while (n > 0)
+			{
+				rslt = String.fromCharCode((n%10)+zero) + rslt;
+				n = n / 10;
+			}
+			return rslt;
+		}
+		
+		/** @private */
+		public static function arabicIndicString(n:int):String
+		{ return createNumericBaseTenString(n,0x660); }
+		/** @private */
+		public static function bengaliString(n:int):String
+		{ return createNumericBaseTenString(n,0x9E6); }
+		/** @private */
+		public static function devanagariString(n:int):String
+		{ return createNumericBaseTenString(n,0x0966); }
+		/** @private */
+		public static function gujaratiString(n:int):String
+		{ return createNumericBaseTenString(n,0xAE6); }
+		/** @private */
+		public static function gurmukhiString(n:int):String
+		{ return createNumericBaseTenString(n,0xA66); }
+		/** @private */
+		public static function kannadaString(n:int):String
+		{ return createNumericBaseTenString(n,0xCE6); }
+		/** @private */
+		public static function persianString(n:int):String
+		{ return createNumericBaseTenString(n,0x6F0); }	// same as urdu
+		/** @private */
+		public static function thaiString(n:int):String
+		{ return createNumericBaseTenString(n,0xE50); }
+		/** @private */
+		public static function urduString(n:int):String
+		{ return createNumericBaseTenString(n,0x6F0); }	// same as persian
+		
+		/** @private */
+		public static function createContinuousAlphaString(n:int,first:int,base:int):String
+		{
+			CONFIG::debug { assert(n > 0,"Bad number passed to createContinuousAlphaString"); }
+			var rslt:String = "";
+			
+			while (n > 0)
+			{
+				rslt = String.fromCharCode(((n-1)%base)+first) + rslt;
+				n = (n-1) / base;
+			}
+			return rslt;
+		}
+	
+		// continuous alphabetic
+		/** @private */
+		public static function lowerAlphaString(n:int):String 
+		{ return createContinuousAlphaString(n,0x61,26); }
+		/** @private */
+		public static function upperAlphaString(n:int):String 
+		{ return createContinuousAlphaString(n,0x41,26); }
+		/** @private */
+		public static function lowerLatinString(n:int):String
+		{ return createContinuousAlphaString(n,0x61,26); }		// same as alpha
+		/** @private */
+		public static function upperLatinString(n:int):String
+		{ return createContinuousAlphaString(n,0x41,26); }		// same as alpha
+		
+		// see http://www.w3.org/TR/css3-lists/#alphabetic for the table definitions
+		/** @private */
+		public static function createTableAlphaString(n:int,table:Vector.<int>):String
+		{
+			CONFIG::debug { assert(n > 0,"Bad number passed to createTableAlphaString"); }
+			var rslt:String = "";
+			var base:int = table.length;
+			
+			while (n > 0)
+			{
+				rslt = String.fromCharCode(table[(n-1)%base]) + rslt;
+				n = (n-1) / base;
+			}
+			return rslt;
+		}
+		
+		// table alphabetic
+		
+		/** @private */
+		public static const cjkEarthlyBranchData:Vector.<int> = Vector.<int>([
+			0x5B50, 0x4E11, 0x5BC5, 0x536F, 0x8FB0, 0x5DF3, 0x5348, 0x672A, 
+			0x7533, 0x9149, 0x620C, 0x4EA5]);
+		/** @private */
+		public static function cjkEarthlyBranchString(n:int):String
+		{ return createTableAlphaString(n,cjkEarthlyBranchData); }
+		
+		/** @private */
+		public static const cjkHeavenlyStemData:Vector.<int> = Vector.<int>([
+			0x7532, 0x4E59, 0x4E19, 0x4E01, 0x620A, 0x5DF1, 0x5E9A, 0x8F9B, 
+			0x58EC, 0x7678 ]);
+		/** @private */
+		public static function cjkHeavenlyStemString(n:int):String
+		{ return createTableAlphaString(n,cjkHeavenlyStemData); }
+
+		/** @private */
+		public static const hangulData:Vector.<int> = Vector.<int>([
+			0xAC00, 0xB098, 0xB2E4, 0xB77C, 0xB9C8, 0xBC14, 0xC0AC, 0xC544, 
+			0xC790, 0xCC28, 0xCE74, 0xD0C0, 0xD30C, 0xD558]);
+		/** @private */
+		public static function hangulString(n:int):String
+		{ return createTableAlphaString(n,hangulData); }
+		
+		/** @private */
+		public static const hangulConstantData:Vector.<int> = Vector.<int>([
+			0x3131, 0x3134, 0x3137, 0x3139, 0x3141, 0x3142, 0x3145, 0x3147, 
+			0x3148, 0x314A, 0x314B, 0x314C, 0x314D, 0x314E]);
+		/** @private */
+		public static function hangulConstantString(n:int):String
+		{ return createTableAlphaString(n,hangulConstantData); }
+		
+		/** @private */
+		public static const hiraganaData:Vector.<int> = Vector.<int>([
+			0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F, 
+			0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F, 
+			0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D, 
+			0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F, 
+			0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A, 
+			0x308B, 0x308C, 0x308D, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093]);
+		/** @private */
+		public static function hiraganaString(n:int):String
+		{ return createTableAlphaString(n,hiraganaData); }
+		
+		/** @private */
+		public static const hiraganaIrohaData:Vector.<int> = Vector.<int>([
+			0x3044, 0x308D, 0x306F, 0x306B, 0x307B, 0x3078, 0x3068, 0x3061, 
+			0x308A, 0x306C, 0x308B, 0x3092, 0x308F, 0x304B, 0x3088, 0x305F, 
+			0x308C, 0x305D, 0x3064, 0x306D, 0x306A, 0x3089, 0x3080, 0x3046, 
+			0x3090, 0x306E, 0x304A, 0x304F, 0x3084, 0x307E, 0x3051, 0x3075, 
+			0x3053, 0x3048, 0x3066, 0x3042, 0x3055, 0x304D, 0x3086, 0x3081, 
+			0x307F, 0x3057, 0x3091, 0x3072, 0x3082, 0x305B, 0x3059 ]);
+		/** @private */
+		public static function hiraganaIrohaString(n:int):String
+		{ return createTableAlphaString(n,hiraganaIrohaData); }
+		
+		/** @private */
+		public static const katakanaData:Vector.<int> = Vector.<int>([
+			0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF, 
+			0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, 0x30BF, 
+			0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD, 
+			0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, 0x30DF, 
+			0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA, 
+			0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3]);
+		/** @private */
+		public static function katakanaString(n:int):String
+		{ return createTableAlphaString(n,katakanaData); }
+		
+		/** @private */
+		public static const katakanaIrohaData:Vector.<int> = Vector.<int>([
+			0x30A4, 0x30ED, 0x30CF, 0x30CB, 0x30DB, 0x30D8, 0x30C8, 0x30C1, 
+			0x30EA, 0x30CC, 0x30EB, 0x30F2, 0x30EF, 0x30AB, 0x30E8, 0x30BF, 
+			0x30EC, 0x30BD, 0x30C4, 0x30CD, 0x30CA, 0x30E9, 0x30E0, 0x30A6, 
+			0x30F0, 0x30CE, 0x30AA, 0x30AF, 0x30E4, 0x30DE, 0x30B1, 0x30D5, 
+			0x30B3, 0x30A8, 0x30C6, 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1, 
+			0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x30B9 ]);
+		/** @private */
+		public static function katakanaIrohaString(n:int):String
+		{ return createTableAlphaString(n,katakanaIrohaData); }
+		
+		/** @private */
+		public static const lowerGreekData:Vector.<int> = Vector.<int>([
+			0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 
+			0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 
+			0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9  ]);
+		/** @private */
+		public static function lowerGreekString(n:int):String
+		{ return createTableAlphaString(n,lowerGreekData); }
+		
+		/** @private */
+		public static const upperGreekData:Vector.<int> = Vector.<int>([
+			0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398,
+			0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0,
+			0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9 ]);
+		/** @private */
+		public static function upperGreekString(n:int):String
+		{ return createTableAlphaString(n,upperGreekData); }
+		
+		/** @private */
+		public static const algorithmicListStyles:Object = {
+			upperRoman:upperRomanString,
+			lowerRoman:lowerRomanString
+		};
+		
+		/** @private */
+		public static const numericListStyles:Object = {
+			arabicIndic:arabicIndicString,
+			bengali:bengaliString,				
+			decimal:decimalString,
+			decimalLeadingZero:decimalLeadingZeroString,
+			devanagari:devanagariString,
+			gujarati:gujaratiString,
+			gurmukhi:gurmukhiString,
+			kannada:kannadaString,
+			persian:persianString,
+			thai:thaiString,
+			urdu:urduString
+		};
+		
+		/** @private */
+		public static const alphabeticListStyles:Object = {
+			upperAlpha:upperAlphaString,
+			lowerAlpha:lowerAlphaString,
+			cjkEarthlyBranch:cjkEarthlyBranchString,
+			cjkHeavenlyStem:cjkHeavenlyStemString,
+			hangul:hangulString,
+			hangulConstant:hangulConstantString,
+			hiragana:hiraganaString,
+			hiraganaIroha:hiraganaIrohaString,
+			katakana:katakanaString,
+			katakanaIroha:katakanaIrohaString,
+			lowerGreek:lowerGreekString,
+			lowerLatin:lowerLatinString,
+			upperGreek:upperGreekString,
+			upperLatin:upperLatinString			
+		};
+
+		// they're all "." right now.  could remove this till its needed
+		/** @private */
+		public static const listSuffixes:Object = {
+			upperAlpha:"\u002E",
+			lowerAlpha:"\u002E",
+			upperRoman:"\u002E",
+			lowerRoman:"\u002E",
+			arabicIndic:"\u002E",
+			bengali:"\u002E",
+			decimal:"\u002E",
+			decimalLeadingZero:"\u002E",
+			devanagari:"\u002E",
+			gujarati:"\u002E",
+			gurmukhi:"\u002E",
+			kannada:"\u002E",
+			persian:"\u002E",
+			thai:"\u002E",
+			urdu:"\u002E",
+			cjkEarthlyBranch:"\u002E",
+			cjkHeavenlyStem:"\u002E",
+			hangul:"\u002E",
+			hangulConstant:"\u002E",
+			hiragana:"\u002E",
+			hiraganaIroha:"\u002E",
+			katakana:"\u002E",
+			katakanaIroha:"\u002E",
+			lowerGreek:"\u002E",
+			lowerLatin:"\u002E",
+			upperGreek:"\u002E",
+			upperLatin:"\u002E"
+		};
+
+		/** @private This function returns the string representing the list item's marker text */
+		public function computeListItemText(child:IListItemElement,listMarkerFormat:IListMarkerFormat):String
+		{
+			CONFIG::debug { assert(child.parent == this,"computeListItemText: bad child"); }
+
+			var listStyleType:String;
+			var rslt:String;
+			
+			if (listMarkerFormat.content && listMarkerFormat.content.hasOwnProperty("counters"))
+			{
+				// chapter style numbering
+				rslt = "";
+				listStyleType = listMarkerFormat.content.ordered;				// may be null
+				var suffixOverride:String = listMarkerFormat.content.suffix;	// may be null
+				
+				var list:ListElement = this;
+				var childListMarkerFormat:IListMarkerFormat = listMarkerFormat;
+				for (;;)
+				{
+					rslt = list.computeListItemTextSpecified(child,childListMarkerFormat,listStyleType == null ? list.computedFormat.listStyleType : listStyleType,suffixOverride) + rslt;
+					// now look up
+					child = list.getParentByType("ListItemElement") as IListItemElement;
+					if (!child)
+						break;
+					list = child.parent as ListElement;
+					childListMarkerFormat = child.computedListMarkerFormat();
+				}
+			}
+			else
+			{
+				if (listMarkerFormat.content !== undefined)
+				{
+					if (listMarkerFormat.content == FormatValue.NONE)
+						listStyleType = ListStyleType.NONE;
+					else 
+					{
+						CONFIG::debug { assert(listMarkerFormat.content.hasOwnProperty("counter"),"Bad ListMarkerFormat.content property"); };
+						listStyleType = listMarkerFormat.content.ordered;
+					}
+				}
+
+				if (listStyleType == null)
+					listStyleType = computedFormat.listStyleType;
+				
+				rslt = computeListItemTextSpecified(child,listMarkerFormat,listStyleType,null);
+			}			
+			var beforeContent:String = listMarkerFormat.beforeContent ? listMarkerFormat.beforeContent : "";
+			var afterContent:String = listMarkerFormat.afterContent ? listMarkerFormat.afterContent : "";
+			return beforeContent + rslt + afterContent;
+		}
+		
+		/** @private - does the work with a known listStyleType and optional suffixOverride. */
+		public function computeListItemTextSpecified(child:IListItemElement,listMarkerFormat:IListMarkerFormat,listStyleType:String,suffixOverride:String):String
+		{
+			var rslt:String;
+			var val:* = constantListStyles[listStyleType];
+			if (val !== undefined)
+				rslt = val as String;
+			else
+			{
+				CONFIG::debug { assert(listSuffixes[listStyleType] !== undefined,"missing suffix"); }
+				
+				var n:int = child.getListItemNumber(listMarkerFormat);
+				var f:Function;
+				
+				// look in the the styles objects
+				f = numericListStyles[listStyleType];
+				if (f != null)
+				{
+					rslt = n < 0 ? "\u002d" + f(-n) : f(n);
+				}
+				else if (n <= 0)
+				{
+					rslt = n == 0 ? "0" : "\u002d" + decimalString(-n);
+				}
+				else
+				{
+					f = alphabeticListStyles[listStyleType];
+					if (f != null)
+						rslt = f(n);
+					else
+						rslt = algorithmicListStyles[listStyleType](n);
+				}
+				if (suffixOverride != null)
+					rslt += suffixOverride;
+				else if (listMarkerFormat.suffix != Suffix.NONE)
+					rslt += listSuffixes[listStyleType];
+			}
+			
+			return rslt;
+		}
+		
+		/** @private */
+		public function isNumberedList():Boolean
+		{ return constantListStyles[computedFormat.listStyleType] === undefined; }
+	}
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/fd08d137/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/elements/ListItemElement.as
----------------------------------------------------------------------
diff --git a/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/elements/ListItemElement.as b/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/elements/ListItemElement.as
new file mode 100644
index 0000000..666ac11
--- /dev/null
+++ b/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/elements/ListItemElement.as
@@ -0,0 +1,275 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package org.apache.flex.textLayout.elements {
+	import org.apache.flex.textLayout.debug.assert;
+	import org.apache.flex.textLayout.formats.BlockProgression;
+	import org.apache.flex.textLayout.formats.FormatValue;
+	import org.apache.flex.textLayout.formats.IListMarkerFormat;
+
+	
+
+	
+	/** 
+	 * <p> ListItemElement is an item in a list. It most commonly contains one or more ParagraphElement objects, but could
+	 * also have children of type DivElement or ListElement. A ListItemElement always appears within a ListElement.</p>
+	 *
+	 * <p>A ListItemElement has automatically generated content that appears before the regular content of the list. This is called
+	 * the <i>marker</i>, and it is what visually distinguishes the list item. The listStyleType property governs how the marker
+	 * is generated and allows the user to control whether the list item is marked with a bullet, a number, or alphabetically.
+	 * The listStylePosition governs where the marker appears relative to the list item; specifically it may appear outside, in the 
+	 * margin of the list, or inside, beside the list item itself. The ListMarkerFormat defines the TextLayoutFormat of the marker
+	 * (by default this will be the same as the list item), as well as an optional suffix that goes at the end of the marker. For 
+	 * instance, for a numbered list, it is common to have a "." as a suffix that appears after the number. The ListMarkerFormat also
+	 * allows specification of text that goes at the start of the marker, and for numbered lists allows control over the numbering.</p>
+	 * 
+	 * @playerversion Flash 10
+	 * @playerversion AIR 1.5
+	 * @langversion 3.0
+	 *
+	 * @see ParagraphElement
+	 * @see org.apache.flex.textLayout.formats.ITextLayoutFormat#listStyleType
+	 * @see org.apache.flex.textLayout.formats.ITextLayoutFormat#listStylePosition
+	 * @see org.apache.flex.textLayout.formats.ListMarkerFormat
+	 */
+	public final class ListItemElement extends ContainerFormattedElement implements IListItemElement
+	{	
+		private const MAX_VALUE:int = 2147483647;
+		/** @private Helps figure out the list number.  Use MAX_VALUE when not set */
+		public var _listNumberHint:int = MAX_VALUE;
+		
+		/** @private */
+		override protected function get abstract():Boolean
+		{ return false; }
+		
+		/** @private */
+		public override function get defaultTypeName():String
+		{ return "li"; }
+		
+		override public function get className():String{
+			return "ListItemElement";
+		}
+
+		/** @private - make more efficient? save and damage results as need be */
+		public function computedListMarkerFormat():IListMarkerFormat
+		{
+			var format:IListMarkerFormat = this.getUserStyleWorker("ListMarkerFormat") as IListMarkerFormat;
+			if (format == null)
+			{
+				var tf:ITextFlow = this.getTextFlow();
+				if (tf)
+					format = tf.configuration.defaultListMarkerFormat;
+			}
+
+			return format;
+		}
+		
+		/**
+		 *  @private ListItems must begin with zero or more divs with a paragraph
+		 * @flexjsignorecoercion org.apache.flex.textLayout.elements.IFlowGroupElement
+		 */
+		public function normalizeNeedsInitialParagraph():Boolean
+		{
+			var p:IFlowGroupElement = this;
+			while (p)
+			{
+				p = p.getChildAt(0) as IFlowGroupElement;
+				if (p is IParagraphElement)
+					return false;
+				if (!(p is IDivElement))
+					return true;
+			}
+			return true;
+		}
+		
+		/** @private */
+		public override function normalizeRange(normalizeStart:uint,normalizeEnd:uint):void
+		{
+			super.normalizeRange(normalizeStart,normalizeEnd);
+			
+			_listNumberHint = MAX_VALUE;
+			
+			// A listItem must have a Paragraph at the start. 
+			// note not all browsers behave this way.
+			if (normalizeNeedsInitialParagraph())
+			{
+				var p:IParagraphElement = ElementHelper.getParagraph();
+				replaceChildren(0,0,p);	
+				p.normalizeRange(0,p.textLength);	
+			}
+		}
+		
+		/** @private */
+		public function getListItemNumber(listMarkerFormat:IListMarkerFormat = null):int
+		{
+			CONFIG::debug { assert(parent != null,"invalid call to ListItemElement.getListItemNumber"); }
+			
+			if (_listNumberHint == MAX_VALUE)
+			{
+				if (listMarkerFormat == null)
+					listMarkerFormat = 	computedListMarkerFormat();
+				
+				var counterReset:Object = listMarkerFormat.counterReset;
+					
+				if (counterReset && counterReset.hasOwnProperty("ordered"))
+					_listNumberHint = counterReset.ordered;
+				else
+				{
+					// search backwards for a ListItemElement and call getListItemNumber on it
+					var idx:int = parent.getChildIndex(this);
+					
+					_listNumberHint = 0;	// if none is found this is zero
+								
+					while (idx > 0)
+					{
+						idx--;
+						var sibling:ListItemElement = parent.getChildAt(idx) as ListItemElement;
+						if (sibling)
+						{
+							_listNumberHint = sibling.getListItemNumber();
+							break;
+						}
+					}
+				}
+				
+				// increment the counter
+				var counterIncrement:Object = listMarkerFormat.counterIncrement;
+				_listNumberHint += (counterIncrement && counterIncrement.hasOwnProperty("ordered")) ? counterIncrement.ordered : 1;
+			}
+
+			return _listNumberHint;
+		}
+		
+		// Fix bug 2800975 ListMarkerFormat.paragraphStartIndent not applied properly in Inside lists.
+		/** @private */
+		public override function getEffectivePaddingLeft():Number
+		{
+			if(getTextFlow().computedFormat.blockProgression == BlockProgression.TB)
+			{
+				if(computedFormat.paddingLeft == FormatValue.AUTO)
+				{
+						if (computedFormat.listMarkerFormat !==undefined && computedFormat.listMarkerFormat.paragraphStartIndent !== undefined)
+						{
+							return  computedFormat.listMarkerFormat.paragraphStartIndent;
+						}
+					return 0;
+						
+				}
+				else
+				{
+					if (computedFormat.listMarkerFormat !==undefined && computedFormat.listMarkerFormat.paragraphStartIndent !== undefined)
+					{
+						return computedFormat.paddingLeft+computedFormat.listMarkerFormat.paragraphStartIndent;
+					}
+					return computedFormat.paddingLeft;
+				}
+			}
+			else
+			{
+				return 0;
+			}
+		}
+		/** @private */
+		public override function getEffectivePaddingTop():Number
+		{
+			if(getTextFlow().computedFormat.blockProgression == BlockProgression.RL)
+			{
+				if(computedFormat.paddingTop == FormatValue.AUTO)
+				{
+					if (computedFormat.listMarkerFormat !==undefined && computedFormat.listMarkerFormat.paragraphStartIndent !== undefined)
+					{
+						return  computedFormat.listMarkerFormat.paragraphStartIndent;
+					}
+					return 0;
+					
+				}
+				else
+				{
+					if (computedFormat.listMarkerFormat !==undefined && computedFormat.listMarkerFormat.paragraphStartIndent !== undefined)
+					{
+						return computedFormat.paddingTop+computedFormat.listMarkerFormat.paragraphStartIndent;
+					}
+					return computedFormat.paddingTop;
+				}
+			}
+			else
+			{
+				return 0;
+			}
+		}
+		/** @private */
+		public override function getEffectivePaddingRight():Number
+		{
+			if(getTextFlow().computedFormat.blockProgression == BlockProgression.TB)
+			{
+				if(computedFormat.paddingRight == FormatValue.AUTO)
+				{
+					if (computedFormat.listMarkerFormat !==undefined && computedFormat.listMarkerFormat.paragraphStartIndent !== undefined)
+					{
+						return  computedFormat.listMarkerFormat.paragraphStartIndent;
+					}
+					return 0;
+					
+				}
+				else
+				{
+					if (computedFormat.listMarkerFormat !==undefined && computedFormat.listMarkerFormat.paragraphStartIndent !== undefined)
+					{
+						return computedFormat.paddingRight+computedFormat.listMarkerFormat.paragraphStartIndent;
+					}
+					return computedFormat.paddingRight;
+				}
+			}
+			else
+			{
+				return 0;
+			}
+		}
+		
+		/** @private */
+		public override function getEffectivePaddingBottom():Number
+		{
+			if(getTextFlow().computedFormat.blockProgression == BlockProgression.RL)
+			{
+				if(computedFormat.paddingBottom == FormatValue.AUTO)
+				{
+					if (computedFormat.listMarkerFormat !==undefined && computedFormat.listMarkerFormat.paragraphStartIndent !== undefined)
+					{
+						return  computedFormat.listMarkerFormat.paragraphStartIndent;
+					}
+					return 0;
+					
+				}
+				else
+				{
+					if (computedFormat.listMarkerFormat !==undefined && computedFormat.listMarkerFormat.paragraphStartIndent !== undefined)
+					{
+						return computedFormat.paddingBottom+computedFormat.listMarkerFormat.paragraphStartIndent;
+					}
+					return computedFormat.paddingBottom;
+				}
+			}
+			else
+			{
+				return 0;
+			}
+		}
+		
+		
+	}
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/fd08d137/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/elements/OverflowPolicy.as
----------------------------------------------------------------------
diff --git a/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/elements/OverflowPolicy.as b/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/elements/OverflowPolicy.as
new file mode 100644
index 0000000..50a3f3c
--- /dev/null
+++ b/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/elements/OverflowPolicy.as
@@ -0,0 +1,63 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package org.apache.flex.textLayout.elements
+{
+	/**
+	 *  The OverflowPolicy class defines a set of constants for the <code>overflowPolicy</code> property
+	 *  of the IConfiguration class. This defines how the composer will treat lines at the end of the composition area.
+	 *
+	 * @playerversion Flash 10
+	 * @playerversion AIR 1.5
+	 * @langversion 3.0
+	 */
+	 
+ 	public final class OverflowPolicy {
+ 	
+	/** 
+	 * Fit the line in the composition area if any part of the line fits.
+	 *
+	 * @playerversion Flash 10
+	 * @playerversion AIR 1.5
+	 * @langversion 3.0
+	 */
+	 
+     public static const FIT_ANY:String = "fitAny";
+    
+	/*
+	 * Fit the line in the composition area if the area from the top to the baseline fits.
+	 *
+	 * @playerversion Flash 10
+	 * @playerversion AIR 1.5
+	 * @langversion 3.0
+	 */
+	 
+ //    public static const FIT_BASELINE:String = "fitAny";
+    
+	/** 
+	 * Fit the line in the composition area if the area from the top to the baseline fits.
+	 *
+	 * @playerversion Flash 10
+	 * @playerversion AIR 1.5
+	 * @langversion 3.0
+	 */
+	 
+     public static const FIT_DESCENDERS:String = "fitDescenders";
+    
+	}
+}

http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/fd08d137/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/elements/ParagraphElement.as
----------------------------------------------------------------------
diff --git a/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/elements/ParagraphElement.as b/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/elements/ParagraphElement.as
new file mode 100644
index 0000000..d72d869
--- /dev/null
+++ b/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/elements/ParagraphElement.as
@@ -0,0 +1,1358 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package org.apache.flex.textLayout.elements 
+{
+	import org.apache.flex.reflection.getQualifiedClassName;
+	import org.apache.flex.text.engine.ContentElement;
+	import org.apache.flex.text.engine.EastAsianJustifier;
+	import org.apache.flex.text.engine.GroupElement;
+	import org.apache.flex.text.engine.ITextBlock;
+	import org.apache.flex.text.engine.ITextLine;
+	import org.apache.flex.text.engine.LineJustification;
+	import org.apache.flex.text.engine.SpaceJustifier;
+	import org.apache.flex.text.engine.TabAlignment;
+	import org.apache.flex.text.engine.TabStop;
+	import org.apache.flex.text.engine.TextRotation;
+	import org.apache.flex.textLayout.compose.ITextFlowLine;
+	import org.apache.flex.textLayout.debug.Debugging;
+	import org.apache.flex.textLayout.debug.assert;
+	import org.apache.flex.textLayout.formats.BlockProgression;
+	import org.apache.flex.textLayout.formats.Direction;
+	import org.apache.flex.textLayout.formats.FormatValue;
+	import org.apache.flex.textLayout.formats.ITextLayoutFormat;
+	import org.apache.flex.textLayout.formats.JustificationRule;
+	import org.apache.flex.textLayout.formats.LeadingModel;
+	import org.apache.flex.textLayout.formats.LineBreak;
+	import org.apache.flex.textLayout.formats.TabStopFormat;
+	import org.apache.flex.textLayout.formats.TextAlign;
+	import org.apache.flex.textLayout.formats.TextJustify;
+	import org.apache.flex.textLayout.formats.TextLayoutFormat;
+	import org.apache.flex.textLayout.property.PropertyUtil;
+	import org.apache.flex.textLayout.utils.CharacterUtil;
+	import org.apache.flex.textLayout.utils.LocaleUtil;
+	
+
+
+	/** 
+	 * The ParagraphElement class represents a paragraph in the text flow hierarchy. Its parent
+	 * is a ParagraphFormattedElement, and its children can include spans (SpanElement), images 
+	 * (inLineGraphicElement), links (LinkElement) and TCY (Tatechuuyoko - ta-tae-chu-yo-ko) elements (TCYElement). The 
+	 * paragraph text is stored in one or more SpanElement objects, which define ranges of text that share the same attributes. 
+	 * A TCYElement object defines a small run of Japanese text that runs perpendicular to the line, as in a horizontal run of text in a 
+	 * vertical line. A TCYElement can also contain multiple spans.
+	 *
+	 * @playerversion Flash 10
+	 * @playerversion AIR 1.5
+	 * @langversion 3.0
+	 * 
+	 * @see InlineGraphicElement
+	 * @see LinkElement
+	 * @see SpanElement
+	 * @see TCYElement
+	 * @see ITextFlow
+	 */
+	 
+	public final class ParagraphElement extends ParagraphFormattedElement implements IParagraphElement	{
+		//private var _textBlock:ITextBlock;
+		private var _terminatorSpan:ISpanElement;
+		
+		private var _interactiveChildrenCount:int;
+		/** Constructor - represents a paragraph in a text flow. 
+		*
+		* @playerversion Flash 10
+		* @playerversion AIR 1.5
+	 	* @langversion 3.0
+	 	*/
+	 	
+		public function ParagraphElement()
+		{
+			super();
+			_terminatorSpan = null;
+			_interactiveChildrenCount = 0 ;
+		}
+		override public function get className():String
+		{
+			return "ParagraphElement";
+		}
+		public function get interactiveChildrenCount():int
+		{
+			return _interactiveChildrenCount;
+		}
+		
+		/** @private */
+		public function createTextBlock():void
+		{
+//			CONFIG::debug { assert(_textBlock == null,"createTextBlock called when there is already a textblock"); }
+			calculateComputedFormat();	// recreate the format BEFORE the _textBlock is created
+			var tbs:Vector.<ITextBlock> = getTextBlocks();
+			//tbs.length = 0;
+			var tableCount:int = 0;
+			if(tbs.length == 0 && !(getChildAt(0) is ITableElement) )
+				tbs.push(getTextFlow().tlfFactory.textFactory.getTextBlock());
+			//getTextBlocks()[0] = new ITextBlock();
+//			CONFIG::debug { Debugging.traceFTECall(_textBlock,null,"new ITextBlock()"); }
+			for (var i:int = 0; i < numChildren; i++)
+			{
+				var child:IFlowElement = getChildAt(i);
+				if(child is ITableElement)
+					tableCount++;
+//					tbs.push(new ITextBlock());
+				else
+				{
+					//child.releaseContentElement();
+					//child.createContentElement();
+				}
+			}
+			while(tableCount >= tbs.length)
+				tbs.push(getTextFlow().tlfFactory.textFactory.getTextBlock());
+			
+			for (i = 0; i < numChildren; i++)
+			{
+				child = getChildAt(i);
+				child.createContentElement();
+			}
+			tbs.length = tableCount + 1;
+			var tb:ITextBlock;
+			for each(tb in tbs){
+				updateTextBlock(tb);
+			}
+		}
+		private function updateTextBlockRefs():void
+		{
+			var tbs:Vector.<ITextBlock> = getTextBlocks();
+			if(tbs.length == 0)
+				return;//nothing to do
+			var tbIdx:int = 0;
+			var tb:ITextBlock = tbs[tbIdx];
+			var items:Array = [];
+			var child:IFlowElement;
+			for (var i:int = 0; i < numChildren; i++)
+			{
+				child = getChildAt(i);
+				if(child is ITableElement)
+				{
+					tb.userData = items;
+					if(++tbIdx == tbs.length)
+						return;
+					tb = tbs[tbIdx];
+					tb.userData = null;
+
+					//Advance to the next one.
+					if(++tbIdx == tbs.length)
+						return;
+					tb = tbs[tbIdx];
+					items = [];
+					continue;
+				}
+				items.push(child);
+			}
+			tb.userData = items;
+		}
+		private function removeTextBlock(tb:ITextBlock):void
+		{
+			var tbs:Vector.<ITextBlock> = getTextBlocks();
+			if(tbs)
+			{
+				var idx:int = getTextBlocks().indexOf(tb);
+				if(idx > -1)
+				{
+					tbs.splice(idx,1);
+				}
+			}
+		}
+		
+		/**
+		 * @flexjsignorecoercion org.apache.flex.textLayout.compose.ITextFlowLine
+		 */
+		private function releaseTextBlockInternal(tb:ITextBlock):void
+		{
+			if (!tb)
+				return;
+			
+			if (tb.firstLine)	// A ITextBlock may have no firstLine if it has previously been released.
+			{
+				for (var textLineTest:ITextLine = tb.firstLine; textLineTest != null; textLineTest = textLineTest.nextLine)
+				{	
+					if(textLineTest.numElements != 0)
+					{	
+						//if the number of adornments added does not match the number of children on the textLine
+						//then a third party has added adornments.  Don't recycle the line or the adornment will be
+						//lost.
+						var tfl:ITextFlowLine = textLineTest.userData as ITextFlowLine;
+						if(tfl.adornCount != textLineTest.numElements)
+							return;
+					}
+				}
+				
+				CONFIG::debug { Debugging.traceFTECall(null,tb,"releaseLines",tb.firstLine, tb.lastLine); }				
+				tb.releaseLines(tb.firstLine, tb.lastLine);	
+			}	
+			var items:Array = tb.userData;
+			if(items)
+			{
+				var len:int = items.length;
+				for (var i:int = 0; i < len; i++)
+				{
+					var child:FlowElement = items[i];
+					child.releaseContentElement();
+				}
+				items.length = 0;
+			}
+			tb.content = null;
+			removeTextBlock(tb);
+		}
+		/** @private */
+		public function releaseTextBlock(tb:ITextBlock=null):void
+		{
+			updateTextBlockRefs();
+			if(tb)
+			{
+				releaseTextBlockInternal(tb);
+				return;
+			}
+			var tbs:Vector.<ITextBlock> = getTextBlocks();
+			for each(var textBlock:ITextBlock in tbs)
+			{
+				releaseTextBlockInternal(textBlock);
+			}
+			//_textBlock = null;
+			if (_computedFormat)
+				_computedFormat = null;
+		}
+		private var _textBlocks:Vector.<ITextBlock>;
+		public function getTextBlocks():Vector.<ITextBlock>
+		{
+			if(_textBlocks == null)
+				_textBlocks = new Vector.<ITextBlock>();
+			return _textBlocks;
+		}
+		/** ITextBlock where the text of the paragraph is kept. @private */
+		public function getTextBlock():ITextBlock
+		{
+			if (!getTextBlocks().length)
+				createTextBlock();
+			
+			return getTextBlocks()[0]; 
+		}
+		/** Last ITextBlock where the text of the paragraph is kept. @private */
+		public function getLastTextBlock():ITextBlock
+		{
+			var tbs:Vector.<ITextBlock> = getTextBlocks();
+			if(!tbs.length)
+				createTextBlock();
+			
+			return tbs[tbs.length-1];
+		}
+
+		/** Get ITextBlock at specified position. @private */
+		public function getTextBlockAtPosition(pos:int):ITextBlock
+		{
+			var curPos:int = 0;
+			var posShift:int = 0;
+			var tables:Vector.<ITableElement> = getTables();
+			if(!tables.length)
+				return getTextBlock();
+			
+			for each(var table:ITableElement in tables)
+			{
+				if(table.getElementRelativeStart(this) < pos)
+					posShift++;
+			}
+			var tbs:Vector.<ITextBlock> = getTextBlocks();
+			for each(var tb:ITextBlock 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;
+		}
+		
+		public function getTextBlockAbsoluteStart(tb:ITextBlock):int
+		{
+			var start:int = getTextBlockStart(tb);
+			if(start < 0)
+				start = 0;
+			return getAbsoluteStart() + start;
+		}
+		public function getTextBlockStart(tb:ITextBlock):int
+		{
+//			var i:int;
+			var curPos:int = 0;
+			var tbs:Vector.<ITextBlock> = getTextBlocks();
+			if(tbs.length == 0)
+				return -1;
+			var tables:Vector.<ITableElement> = getTables();
+			for each(var curTB:ITextBlock in tbs)
+			{
+				for each(var table:ITableElement 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.<ITableElement>
+		{
+			var tables:Vector.<ITableElement> = new Vector.<ITableElement>();
+			for (var i:int = 0; i < numChildren; i++)
+			{
+				var child:IFlowElement = getChildAt(i);
+				if(child is ITableElement)
+					tables.push(child as ITableElement);
+			}
+			return tables;
+		}
+
+		/** ITextBlock where the text of the paragraph is kept, or null if we currently don't have one. @private */
+		public function peekTextBlock():ITextBlock
+		{ 
+			return getTextBlocks().length == 0 ? null : getTextBlocks()[0];
+		}
+		
+		/** @private */
+		public function releaseLineCreationData():void
+		{
+			// CONFIG::debug { assert(Configuration.playerEnablesArgoFeatures,"bad call to releaseLineCreationData"); }
+			var tbs:Vector.<ITextBlock> = getTextBlocks();
+			for each(var tb:ITextBlock in tbs)
+			{
+				tb["releaseLineCreationData"]();
+			}
+		}
+		
+		/** @private */
+		public override function createContentAsGroup(pos:int=0):GroupElement
+		{
+			var tb:ITextBlock = getTextBlockAtPosition(pos);
+			if(!tb)
+				tb = getTextBlockAtPosition(pos-1);
+			var group:GroupElement = tb.content as GroupElement;
+			if (!group)
+			{
+				var originalContent:ContentElement = tb.content;
+				
+				group = new GroupElement();
+				CONFIG::debug { Debugging.traceFTECall(group,null,"new GroupElement()"); }
+				tb.content = group;
+				CONFIG::debug { Debugging.traceFTEAssign(tb,"content",group); }
+
+				if (originalContent)
+				{
+					var gc:Vector.<ContentElement> = new Vector.<ContentElement>();
+					CONFIG::debug { Debugging.traceFTECall(gc,null,"new Vector.<ContentElement>()"); }
+					gc.push(originalContent);
+					CONFIG::debug { Debugging.traceFTECall(null,gc,"push",originalContent); }
+					group.replaceElements(0,0,gc);
+					CONFIG::debug { Debugging.traceFTECall(null,group,"replaceElements",0,0,gc); }
+				}
+				
+				// Now we've got to force damage the entire paragraph, because we restructured it in FTE.
+				if (tb.firstLine && textLength)
+				{
+					var textFlow:ITextFlow = getTextFlow();
+					if (textFlow)
+						textFlow.damage(getAbsoluteStart(), textLength, "invalid", false);
+				}
+			}
+			return group;
+ 		}
+ 		
+ 		/** @private */
+		public override function removeBlockElement(child:IFlowElement, block:ContentElement):void
+		{
+			var tb:ITextBlock = 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)
+				{
+					// see insertBlockElement
+//					CONFIG::debug { assert(_textBlock.content != block,"removeBlockElement: bad call to removeBlockElement"); }
+//					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(tb.content).replaceElements(0,1,null);
+//					CONFIG::debug { Debugging.traceFTECall(null,_textBlock.content,"replaceElements",0,1,null); }
+				}
+				tb.content = null;
+//				CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"content",null); }
+			}
+			else if(block.groupElement)
+			{
+				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
+				{
+					// ungroup - need to take it out first as inlinelements can only have one parent
+					var elem:ContentElement = group.getElementAt(0);
+					CONFIG::debug { Debugging.traceFTECall(elem,group,"getElementAt",0); }
+					if (!(elem is GroupElement))
+					{
+						group.replaceElements(0,1,null);
+						CONFIG::debug { Debugging.traceFTECall(null,group,"replaceElements",0,1,null); }
+						tb.content = elem;
+						CONFIG::debug { Debugging.traceFTEAssign(tb,"content",elem); }
+					}
+				}
+			}
+			else {
+				//trace("1");
+				//tb.content = null;
+			}
+		}
+		
+		
+		/** @private */
+		public override function hasBlockElement():Boolean
+		{
+			return getTextBlocks().length > 0;
+		}
+		
+		/** @private */
+		override public function createContentElement():void
+		{
+			createTextBlock();
+		}
+		
+		/** @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 ITableElement)
+				{
+					if(chldrn[i].parentRelativeStart < pos)
+					{
+						retVal.length = 0;
+						continue;
+					}
+					if(chldrn[i].parentRelativeStart >= pos)
+						break;
+				}
+				retVal.push(chldrn[i]);		
+			}
+			return retVal;
+		}
+		
+		/** @private */
+		public override function insertBlockElement(child:IFlowElement, block:ContentElement):void
+		{
+			var relativeStart:int = child.getElementRelativeStart(this);
+			var tb:ITextBlock = getTextBlockAtPosition(relativeStart);
+			if(!tb)
+				tb = getTextBlockAtPosition(relativeStart-1);
+			
+			if(!tb)
+			{
+				child.releaseContentElement();
+				return;
+			}
+			if (getTextBlocks().length == 0)
+			{
+				child.releaseContentElement();
+				createTextBlock();	// does the whole tree
+				return;
+			}
+			var gc:Vector.<ContentElement>;	// scratch var
+			var group:GroupElement;			// scratch
+			if (getChildrenInTextBlock(relativeStart).length < 2)
+			{
+				if (block is GroupElement)
+				{
+					// this case forces the Group to be in a Group so that following FlowLeafElements aren't in a SubParagraphGroupElementBase's group
+					gc = new Vector.<ContentElement>();
+					CONFIG::debug { Debugging.traceFTECall(gc,null,"new Vector.<ContentElement>()"); }
+					gc.push(block);
+					CONFIG::debug { Debugging.traceFTECall(null,gc,"push",block); }
+					group = new GroupElement(gc);
+					CONFIG::debug { Debugging.traceFTECall(group,null,"new GroupElement",gc); }
+					tb.content = group;
+//					CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"content",group); }
+				}
+				else
+				{
+//TODO commented this out. Was there any reason it was here?
+					// if(block.groupElement)
+					// {
+					// 	block.groupElement.elementCount;
+					// }
+					tb.content = block;
+//					CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"content",block);  }
+				}
+			}
+			else
+			{
+				group = createContentAsGroup(relativeStart);
+				var idx:int = getChildIndexInBlock(child);
+				gc = new Vector.<ContentElement>();
+				CONFIG::debug { Debugging.traceFTECall(gc,null,"new Vector.<ContentElement>"); }
+				gc.push(block);
+				CONFIG::debug { Debugging.traceFTECall(null,gc,"push",block); }
+				// If elements in the middle (i.e. ones in the process of being added) were missed, the idx can be too high.
+				// The missed ones will be inserted later.
+				if(idx > group.elementCount)
+					idx = group.elementCount;
+				group.replaceElements(idx,idx,gc);
+				CONFIG::debug { Debugging.traceFTECall(null,group,"replaceElements",idx,idx,gc); }
+			}
+		}
+		
+		private function getChildIndexInBlock(elem:IFlowElement):int
+		{
+			var relIdx:int = 0;
+			for (var i:int = 0; i < numChildren; i++)
+			{
+				var child:IFlowElement = getChildAt(i);
+				if(child == elem)
+					return relIdx;
+				relIdx++;
+				if(child is ITableElement)
+					relIdx = 0;
+			}
+			return -1;
+		}
+		
+		/** @private */
+		override protected function get abstract():Boolean
+		{ return false;	}	
+		
+		/** @private */
+		public override function get defaultTypeName():String
+		{ return "p"; }
+
+		public 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;
+
+			do{
+				if(_terminatorSpan)
+				{
+					var termIdx:int = getChildIndex(_terminatorSpan);
+					if(termIdx > 0 && termIdx < beginChildIndex && _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);
+				}
+//TODO fix super				
+				super.replaceChildren.apply(this, applyParams);
+				
+			}while(false);
+			
+			ensureTerminatorAfterReplace();
+			// ensure correct text blocks
+			createTextBlock();
+		}
+		
+		public override function splitAtPosition(relativePosition:int):IFlowElement
+		{
+			// need to handle multiple TextBlocks
+			// maybe not. It might be handled in replaceChildren().
+			return super.splitAtPosition(relativePosition);
+		}
+		/**
+		 *  @private
+		 * @flexjsignorecoercion org.apache.flex.textLayout.elements.ISpanElement
+		 */
+		public function ensureTerminatorAfterReplace():void
+		{
+			//lose reference to terminator if it was removed or not a direct child.
+			if(_terminatorSpan && _terminatorSpan.parent != this)
+			{
+				_terminatorSpan.removeParaTerminator();
+				_terminatorSpan = null;
+			}
+			
+			var newLastLeaf:IFlowLeafElement = getLastLeaf();
+			if (_terminatorSpan != newLastLeaf)
+			{
+				if(_terminatorSpan)
+					_terminatorSpan.removeParaTerminator();
+				if (newLastLeaf && _terminatorSpan)
+				{
+					if(_terminatorSpan.textLength == 0 && !_terminatorSpan.id)
+					{
+						var termIdx:int = getChildIndex(_terminatorSpan);
+						super.replaceChildren(termIdx, termIdx+1);
+					}
+					this._terminatorSpan = null;
+				}
+				if(newLastLeaf)
+				{
+					if (newLastLeaf is ISpanElement)
+					{
+						(newLastLeaf as ISpanElement).addParaTerminator();
+						this._terminatorSpan = newLastLeaf as ISpanElement;
+					}
+					else
+					{
+						var s:ISpanElement = ElementHelper.getTerminator(this, newLastLeaf);
+						super.replaceChildren(numChildren,numChildren,s);
+						this._terminatorSpan = s;
+					}
+				}
+				else
+					_terminatorSpan = null;
+			}
+			//merge terminator span to previous if possible
+			if(_terminatorSpan && _terminatorSpan.textLength == 1)
+			{
+				var prev:IFlowLeafElement = _terminatorSpan.getPreviousLeaf(this);
+				if(prev && prev.parent == this && prev is ISpanElement)
+				{
+					_terminatorSpan.mergeToPreviousIfPossible();
+				}
+			}
+		}
+		
+		/** @private */
+		public function updateTerminatorSpan(splitSpan:ISpanElement,followingSpan:ISpanElement):void
+		{
+			if (_terminatorSpan == splitSpan)
+				_terminatorSpan = followingSpan;
+		}
+
+		[RichTextContent]
+		/** @private NOTE: all FlowElement implementers and overrides of mxmlChildren must specify [RichTextContent] metadata */
+		public override function set mxmlChildren(array:Array):void
+		{
+			// remove all existing children
+			replaceChildren(0,numChildren);
+			
+			for each (var child:Object in array)
+			{
+				if (child is IFlowElement)
+				{
+					if ((child is ISpanElement) || (child is ISubParagraphGroupElementBase))
+						child.bindableElement = true;
+					
+					// Note: calling super.replaceChildren because we don't want to transfer para terminator each time
+					super.replaceChildren(numChildren, numChildren, child as IFlowElement);
+				}
+				else if (child is String)
+				{
+					var s:ISpanElement = ElementHelper.getSpan();
+					s.text = String(child);
+					s.bindableElement = true;
+					
+					// Note: calling super.replaceChildren because we don't want to transfer para terminator each time
+					super.replaceChildren(numChildren, numChildren, s);
+				}
+				else if (child != null)
+					throw new TypeError(GlobalSettings.resourceStringFunction("badMXMLChildrenArgument",[ getQualifiedClassName(child) ]));
+			}
+			
+			// Now ensure para terminator
+			ensureTerminatorAfterReplace();
+			
+			// recreate text blocks to handle possible TableElement changes
+			createTextBlock();
+		}
+		
+		/** @private
+ 		 */
+		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) && getTextBlocks().length)
+			{
+				var tb:ITextBlock;
+				var tbs:Vector.<ITextBlock> = getTextBlocks();
+				var text:String = "";
+				for each(tb in tbs)
+				{
+					text = text + getTextInBlock(tb);
+				}
+				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:ITextBlock):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. 
+		 *
+		 * @return the next paragraph or null if there are no more paragraphs.
+		 *
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+	 	 * @langversion 3.0
+	 	 * 
+	 	 * @see #getPreviousParagraph()
+		 *  @flexjsignorecoercion org.apache.flex.textLayout.elements.ParagraphElement
+		 */
+		public function getNextParagraph():IParagraphElement
+		{
+			var nextLeaf:IFlowLeafElement = getLastLeaf().getNextLeaf();
+			return nextLeaf ? nextLeaf.getParagraph() as ParagraphElement : null;
+		}
+	
+		/** Returns the paragraph that precedes this one, or null, if this paragraph is the first one 
+		 * in the ITextFlow. 
+		 *
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+	 	 * @langversion 3.0
+	 	 *
+	 	 * @see #getNextParagraph()
+	 	 */
+		public function getPreviousParagraph():IParagraphElement
+		{
+			var previousLeaf:IFlowLeafElement = getFirstLeaf().getPreviousLeaf();
+			return previousLeaf ? previousLeaf.getParagraph() : null;
+		}
+	
+		/** 
+		 * Scans backward from the supplied position to find the location
+		 * in the text of the previous atom and returns the index. The term atom refers to 
+		 * both graphic elements and characters (including groups of combining characters), the 
+		 * indivisible entities that make up a text line.
+		 * 
+		 * @param relativePosition  position in the text to start from, counting from 0
+		 * @return index in the text of the previous cluster
+		 *
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+	 	 * @langversion 3.0
+	 	 *
+	 	 * @see org.apache.flex.text.engine.ITextLine
+		 */
+		 
+		public function findPreviousAtomBoundary(relativePosition:int):int
+		{
+			var tb:ITextBlock = getTextBlockAtPosition(relativePosition);
+			if(!tb || !tb.content)
+				return relativePosition-1;
+			
+			var tbStart:int = getTextBlockStart(tb);
+			var textBlockPos:int = relativePosition - tbStart;
+            var tl:ITextLine = tb.getTextLineAtCharIndex(textBlockPos);
+			if (ConfigSettings.usesDiscretionaryHyphens && tl != null)
+			{
+				var currentAtomIndex:int = tl.getAtomIndexAtCharIndex(textBlockPos);
+                //trace("relpos", relativePosition, "atomIndex", currentAtomIndex);
+                var isRTL:Boolean = tl.getAtomBidiLevel(currentAtomIndex) == 1;
+                if (isRTL)
+                {
+//                   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 (--textBlockPos)
+                           {
+							   --relativePosition;
+                               if (tl.getAtomIndexAtCharIndex(textBlockPos) != currentAtomIndex)
+                                   break;
+                           }
+                       }
+                   }
+                   else
+                   {
+                       while (--relativePosition && --textBlockPos)
+                       {
+                           if (tl.getAtomIndexAtCharIndex(textBlockPos) != currentAtomIndex)
+                               break;
+                       }
+                   }
+                   if (CharacterUtil.isLowSurrogate(getText(relativePosition, relativePosition + 1).charCodeAt(0)))
+				   {
+					   relativePosition--;
+					   textBlockPos--;
+				   }
+				   
+                   //trace("previous", relativePosition, foo);
+                }
+                else
+                {
+    				if (currentAtomIndex == 0)
+    				{
+    					tl = tl.previousLine;
+    					if (!tl)
+						{
+							if(tb != _textBlocks[0])
+								return relativePosition-1;
+							return -1;
+						}
+    					// need this when 0x2028 line separator in use
+    					if (tl.textBlockBeginIndex + tl.rawTextLength == textBlockPos)
+    						return tl.textBlockBeginIndex + tl.rawTextLength - 1 + tbStart;
+    					return tl.textBlockBeginIndex + tl.rawTextLength + tbStart;
+    				}
+    				while (--relativePosition && --textBlockPos)
+    				{
+    					if (tl.getAtomIndexAtCharIndex(textBlockPos) < currentAtomIndex)
+    						break;
+    				}
+                    if (CharacterUtil.isLowSurrogate(getText(relativePosition, relativePosition + 1).charCodeAt(0)))
+					{
+						relativePosition--;
+						textBlockPos--;
+					}
+                }
+				return relativePosition;
+			}
+            var pos:int = tb.findPreviousAtomBoundary(textBlockPos);
+			if(pos >= 0)
+				pos += tbStart;
+            //trace("previous", relativePosition, pos);
+			return pos;
+		}
+
+		/** 
+		 * Scans ahead from the supplied position to find the location
+		 * in the text of the next atom and returns the index. The term atom refers to 
+		 * both graphic elements and characters (including groups of combining characters), the 
+		 * indivisible entities that make up a text line.
+		 * 
+		 * @param relativePosition  position in the text to start from, counting from 0
+		 * @return index in the text of the following atom
+		 *
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+	 	 * @langversion 3.0
+	 	 *
+	 	 * @see org.apache.flex.text.engine.ITextLine
+		 */
+		 
+		public function findNextAtomBoundary(relativePosition:int):int
+		{
+			var tb:ITextBlock = getTextBlockAtPosition(relativePosition);
+			if(!tb || !tb.content)
+				return relativePosition+1;
+			var tbStart:int = getTextBlockStart(tb);
+			var textBlockPos:int = relativePosition - tbStart;
+            var tl:ITextLine = tb.getTextLineAtCharIndex(textBlockPos);
+			if (ConfigSettings.usesDiscretionaryHyphens && tl != null)
+			{
+				var currentAtomIndex:int = tl.getAtomIndexAtCharIndex(textBlockPos);
+                //trace("relpos", relativePosition, "atomIndex", currentAtomIndex);
+                var isRTL:Boolean = tl.getAtomBidiLevel(currentAtomIndex) == 1;
+                if (isRTL)
+                {
+//                    var foo:int = tb.findNextAtomBoundary(textBlockPos);
+                    if (currentAtomIndex == 0)
+                    {
+                        while (++textBlockPos)
+                        {
+							++relativePosition;
+                            if (tl.getAtomIndexAtCharIndex(textBlockPos) != currentAtomIndex)
+                                break;
+                        }
+                    }
+                    else
+                    {
+                        while (++textBlockPos)
+                        {
+							++relativePosition;
+                            if (tl.getAtomIndexAtCharIndex(textBlockPos) != currentAtomIndex)
+                                break;
+                        }
+                    }
+                    if (CharacterUtil.isHighSurrogate(getText(relativePosition, relativePosition + 1).charCodeAt(0)))
+					{
+						relativePosition++;
+						textBlockPos++;
+					}
+                    //trace("next", relativePosition, foo);
+                }
+                else
+                {
+    				if (currentAtomIndex == tl.atomCount - 1)
+    				{
+    					tl = tl.nextLine;
+    					if (!tl)
+						{
+							if(tb != _textBlocks[_textBlocks.length-1])
+								return relativePosition+1;
+							return -1;
+						}
+    					return tl.textBlockBeginIndex + tbStart;
+    				}
+    				while (++textBlockPos)
+    				{
+						++relativePosition;
+    					if (tl.getAtomIndexAtCharIndex(textBlockPos) > currentAtomIndex)
+    						break;
+    				}
+                    if (CharacterUtil.isHighSurrogate(getText(relativePosition, relativePosition + 1).charCodeAt(0)))
+					{
+						relativePosition++;
+						textBlockPos++;
+					}
+                }
+				return relativePosition;
+			}
+			var pos:int = tb.findNextAtomBoundary(textBlockPos);
+			if(pos >= 0)
+				pos += tbStart;
+            //trace("next", relativePosition, pos);
+            return pos;
+		}
+		
+		/** @private */
+		public override function getCharAtPosition(relativePosition:int):String
+		{
+			var foundTB:ITextBlock = getTextBlockAtPosition(relativePosition);
+			if(!foundTB)
+				return "\u0016";
+			var tables:Vector.<ITableElement> = getTables();
+			var pos:int = relativePosition;
+			for each(var table:ITableElement in tables)
+			{
+				if(table.getElementRelativeStart(this) < pos)
+					relativePosition--;
+			}
+			var tbs:Vector.<ITextBlock> = getTextBlocks();
+			for each(var tb:ITextBlock in tbs)
+			{
+				if(foundTB == tb)
+					break;
+				if(tb)
+					relativePosition -= tb.content.rawText.length;
+				else
+					relativePosition -= 1;//TODO was this needed? this.getText()
+			}
+			return foundTB.content.rawText.charAt(relativePosition);
+		} 
+
+		/** 
+		 * Returns the index of the previous word boundary in the text.
+		 * 
+		 * <p>Scans backward from the supplied position to find the previous position
+		 * in the text that starts or ends a word. </p>
+		 * 
+		 * @param relativePosition  position in the text to start from, counting from 0
+		 * @return index in the text of the previous word boundary
+		 *
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+	 	 * @langversion 3.0
+		 */
+		 
+		public function findPreviousWordBoundary(relativePosition:int):int
+		{	
+			if (relativePosition == 0)
+				return 0;
+			var prevCharCode:int = getCharCodeAtPosition(relativePosition - 1);
+			if (CharacterUtil.isWhitespace(prevCharCode))
+			{				
+				while (CharacterUtil.isWhitespace(prevCharCode) && ((relativePosition - 1) > 0))
+				{
+					relativePosition--;
+					prevCharCode = getCharCodeAtPosition(relativePosition - 1);
+				}
+				return relativePosition;
+			}
+			var block:ITextBlock = 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);
+		}
+
+		/** 
+		 * Returns the index of the next word boundary in the text.
+		 * 
+		 * <p>Scans ahead from the supplied position to find the next position
+		 * in the text that starts or ends a word. </p>
+		 * 
+		 * @param relativePosition  position in the text to start from, counting from 0
+		 * @return  index in the text of the next word boundary
+		 * 
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+	 	 * @langversion 3.0
+		 */
+		 
+		public function findNextWordBoundary(relativePosition:int):int
+		{	
+			if (relativePosition == textLength) 
+				return textLength;
+			var curCharCode:int = getCharCodeAtPosition(relativePosition);
+			if (CharacterUtil.isWhitespace(curCharCode))
+			{
+				while (CharacterUtil.isWhitespace(curCharCode) && relativePosition < (textLength - 1))
+				{
+					relativePosition++;
+					curCharCode = getCharCodeAtPosition(relativePosition);
+				}
+				return relativePosition;
+			}
+			var block:ITextBlock = 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>;
+		static private const defaultTabWidth:int = 48;		// matches default tabs setting in Argo
+		static private const defaultTabCount:int = 20;
+		
+		static private function initializeDefaultTabStops():void
+		{
+			_defaultTabStops = new Vector.<TabStop>(defaultTabCount, true);
+			for (var i:int = 0; i < defaultTabCount; ++i)
+				_defaultTabStops[i] = new TabStop(TextAlign.START, defaultTabWidth * i);
+		}
+		
+		private function updateTextBlock(textBlock:ITextBlock=null):void
+		{
+			if(!textBlock)
+				textBlock = getTextBlock();
+			// find the ancestor with a container and use its format for various settings
+			var containerElement:IContainerFormattedElement = getAncestorWithContainer();
+			if (!containerElement)
+				return;
+				
+			var containerElementFormat:ITextLayoutFormat = containerElement ? containerElement.computedFormat : TextLayoutFormat.defaultFormat;
+			
+			var lineJust:String;
+			if (computedFormat.textAlign == TextAlign.JUSTIFY)
+			{
+				lineJust = (_computedFormat.textAlignLast == TextAlign.JUSTIFY) ?
+					LineJustification.ALL_INCLUDING_LAST :
+					LineJustification.ALL_BUT_LAST;
+					
+				// We don't allow explicit line breaks and justification together because it results in very strange (invisible) lines
+				if (containerElementFormat.lineBreak == LineBreak.EXPLICIT)
+					lineJust = LineJustification.UNJUSTIFIED;
+			}
+			else
+				lineJust = LineJustification.UNJUSTIFIED;
+		
+			
+			var makeJustRuleStyle:String = this.getEffectiveJustificationStyle();
+			
+			var justRule:String = this.getEffectiveJustificationRule();
+				
+			// set the justifier in the ITextBlock
+			if (justRule == JustificationRule.SPACE)
+			{
+				var spaceJustifier:SpaceJustifier = new SpaceJustifier(_computedFormat.locale,lineJust,false);
+				spaceJustifier.letterSpacing = _computedFormat.textJustify == TextJustify.DISTRIBUTE ? true : false;
+
+				// These three properties have to be set in the correct order so that consistency checks done
+				// in the Player on set are never violated
+				var newMinimumSpacing:Number = PropertyUtil.toNumberIfPercent(_computedFormat.wordSpacing.minimumSpacing)/100;
+				var newMaximumSpacing:Number = PropertyUtil.toNumberIfPercent(_computedFormat.wordSpacing.maximumSpacing)/100;
+				var newOptimumSpacing:Number = PropertyUtil.toNumberIfPercent(_computedFormat.wordSpacing.optimumSpacing)/100; 
+				spaceJustifier.minimumSpacing = Math.min(newMinimumSpacing, spaceJustifier.minimumSpacing);
+				spaceJustifier.maximumSpacing = Math.max(newMaximumSpacing, spaceJustifier.maximumSpacing);
+				spaceJustifier.optimumSpacing = newOptimumSpacing;
+				spaceJustifier.minimumSpacing = newMinimumSpacing;
+				spaceJustifier.maximumSpacing = newMaximumSpacing;
+
+				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 = LeadingUtils.getLeadingBasis(this.getEffectiveLeadingModel());
+				CONFIG::debug { Debugging.traceFTEAssign(textBlock,"baselineZero",textBlock.baselineZero);  }
+			}
+			else
+			{
+				var eastAsianJustifier:Object = new EastAsianJustifier(_computedFormat.locale,lineJust, makeJustRuleStyle);
+				if( /*Configuration.versionIsAtLeast(10,3) &&*/ eastAsianJustifier.hasOwnProperty("composeTrailingIdeographicSpaces")){
+					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 = LeadingUtils.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.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
+				var tabStops:Vector.<TabStop> = new Vector.<TabStop>();
+				CONFIG::debug { Debugging.traceFTECall(tabStops,null,"new Vector.<TabStop>()"); }
+				for each(var tsa:TabStopFormat in _computedFormat.tabStops)
+				{
+					var token:String = tsa.decimalAlignmentToken==null ? "" : tsa.decimalAlignmentToken;
+					var alignment:String = tsa.alignment==null ? TabAlignment.START : tsa.alignment;
+					var tabStop:TabStop = new TabStop(alignment,Number(tsa.position),token);
+					// this next line when uncommented works around bug 1912782
+					if (tsa.decimalAlignmentToken != null) var garbage:String = "x" + tabStop.decimalAlignmentToken;
+					CONFIG::debug { Debugging.traceFTECall(tabStop,null,"new TabStop",tabStop.alignment,tabStop.position,tabStop.decimalAlignmentToken); }
+					tabStops.push(tabStop);
+					CONFIG::debug { Debugging.traceFTECall(null,tabStops,"push",tabStop); }
+				}
+				textBlock.tabStops = tabStops;
+				CONFIG::debug { Debugging.traceFTEAssign(textBlock,"tabStops",tabStops);  }
+			} //TODO make sure the Text Engine sets default tab stops
+			// else if (GlobalSettings.enableDefaultTabStops && !Configuration.playerEnablesArgoFeatures)
+			// {
+			// 	// 	Player versions prior to 10.1 do not set up any default tabStops. As a workaround, if enableDefaultTabs
+			// 	//	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);  }
+			// }
+			else
+			{
+				textBlock.tabStops = null;
+				CONFIG::debug { Debugging.traceFTEAssign(textBlock,"tabStops",null);  }
+			}		 
+		}
+		
+		/** @private */
+		public override function get computedFormat():ITextLayoutFormat
+		{
+			if (!_computedFormat)
+			{
+				super.computedFormat;
+				var tbs:Vector.<ITextBlock> = getTextBlocks();
+				for each(var tb:ITextBlock in tbs)
+					updateTextBlock(tb);
+					
+			}
+			return _computedFormat;
+		}
+
+		/** @private */
+		public override function canOwnFlowElement(elem:IFlowElement):Boolean
+		{
+			return elem is IFlowLeafElement || elem is ISubParagraphGroupElementBase || elem is ITableElement;
+		}
+		
+		/** @private */
+		public override function normalizeRange(normalizeStart:uint,normalizeEnd:uint):void
+		{
+			var idx:int = findChildIndexAtPosition(normalizeStart);
+			if (idx != -1 && idx < numChildren)
+			{
+				var child:IFlowElement = getChildAt(idx);
+				normalizeStart = normalizeStart-child.parentRelativeStart;
+				
+				CONFIG::debug { assert(normalizeStart >= 0, "bad normalizeStart in normalizeRange"); }
+				for (;;)
+				{
+					// watch out for changes in the length of the child
+					var origChildEnd:int = child.parentRelativeStart+child.textLength;
+					child.normalizeRange(normalizeStart,normalizeEnd-child.parentRelativeStart);
+					var newChildEnd:int = child.parentRelativeStart+child.textLength;
+					normalizeEnd += newChildEnd-origChildEnd;	// adjust
+					
+					// no zero length children
+					if (child.textLength == 0 && !child.bindableElement)
+						replaceChildren(idx,idx+1);
+					else if (child.mergeToPreviousIfPossible())
+					{
+						var prevElement:IFlowElement = this.getChildAt(idx-1);
+						// possibly optimize the start to the length of prevelement before the merge
+						prevElement.normalizeRange(0,prevElement.textLength);
+					}
+					else
+						idx++;
+
+					if (idx == numChildren)
+					{
+						// check if last child is an empty SubPargraphBlock and remove it
+						if (idx != 0)
+						{
+							var lastChild:IFlowElement = this.getChildAt(idx-1);
+							if (lastChild is ISubParagraphGroupElementBase && lastChild.textLength == 1 && !lastChild.bindableElement)
+								replaceChildren(idx-1,idx);
+						}
+						break;
+					}
+					
+					// next child
+					child = getChildAt(idx);
+					
+					if (child.parentRelativeStart > normalizeEnd)
+						break;
+						
+					normalizeStart = 0;		// for the next child	
+				}
+			}
+			
+			// empty paragraphs not allowed after normalize
+			if (numChildren == 0 || textLength == 0)
+			{
+				var s:ISpanElement = ElementHelper.getSpan();
+				replaceChildren(0,0,s);
+				s.normalizeRange(0,s.textLength);
+			}
+		}
+		
+		/** @private */
+		public function getEffectiveLeadingModel():String
+		{
+			return computedFormat.leadingModel == LeadingModel.AUTO ? LocaleUtil.leadingModel(computedFormat.locale) : computedFormat.leadingModel;
+		}
+		
+		/** @private */
+		public function getEffectiveDominantBaseline():String
+		{
+			return computedFormat.dominantBaseline == FormatValue.AUTO ? LocaleUtil.dominantBaseline(computedFormat.locale) : computedFormat.dominantBaseline;
+		}
+		
+		/** @private */
+		public function getEffectiveJustificationRule():String
+		{
+			return computedFormat.justificationRule == FormatValue.AUTO ? LocaleUtil.justificationRule(computedFormat.locale) : computedFormat.justificationRule;
+		}
+		
+		/** @private */
+		public function getEffectiveJustificationStyle():String
+		{
+			return computedFormat.justificationStyle == FormatValue.AUTO ? LocaleUtil.justificationStyle(computedFormat.locale) : computedFormat.justificationStyle;
+		}
+		
+		
+		/** @private */
+		CONFIG::debug public override function debugCheckFlowElement(depth:int = 0, extraData:String = ""):int
+		{
+			var tb:ITextBlock = getTextBlock();
+			var rslt:int = super.debugCheckFlowElement(depth," fte:"+getDebugIdentity(tb)+" "+extraData);
+			
+			// now check the character count and then the last character 
+			
+			if (tb)
+			{
+				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 = tb.content as GroupElement;
+				if (groupElement)
+					assert(groupElement.elementCount == numChildren,"Mismatched group and elementCount"); 
+				else if (tb.content)
+					assert(1 == numChildren,"Mismatched group and elementCount"); 
+				else 
+					assert(0 == numChildren,"Mismatched group and elementCount"); 
+			}
+			rslt += assert(numChildren == 0 || textLength > 0,"Para must have at least one text char");
+			return rslt;
+		}
+		
+
+		
+		public function incInteractiveChildrenCount() : void
+		{
+			++ _interactiveChildrenCount ;
+		}
+		public function decInteractiveChildrenCount() : void
+		{
+			-- _interactiveChildrenCount ;
+		}
+		
+		public function hasInteractiveChildren() : Boolean
+		{
+			return _interactiveChildrenCount != 0 ;
+		}
+
+		public function get terminatorSpan():ISpanElement
+		{
+			return _terminatorSpan;
+		}
+
+	}
+}