1////////////////////////////////////////////////////////////////////////////////
2//
3// ADOBE SYSTEMS INCORPORATED
4// Copyright 2007-2010 Adobe Systems Incorporated
5// All Rights Reserved.
6//
7// NOTICE:  Adobe permits you to use, modify, and distribute this file
8// in accordance with the terms of the license agreement accompanying it.
9//
10////////////////////////////////////////////////////////////////////////////////
11package flashx.textLayout.elements
12{
13	import flash.display.DisplayObjectContainer;
14	import flash.events.IEventDispatcher;
15	import flash.utils.Dictionary;
16	import flash.utils.getDefinitionByName;
17	import flash.utils.getQualifiedClassName;
18
19	import flashx.textLayout.compose.IFlowComposer;
20	import flashx.textLayout.container.ContainerController;
21	import flashx.textLayout.debug.Debugging;
22	import flashx.textLayout.debug.assert;
23	import flashx.textLayout.events.ModelChange;
24	import flashx.textLayout.formats.FormatValue;
25	import flashx.textLayout.formats.ITextLayoutFormat;
26	import flashx.textLayout.formats.ListMarkerFormat;
27	import flashx.textLayout.formats.TextLayoutFormat;
28	import flashx.textLayout.property.Property;
29	import flashx.textLayout.tlf_internal;
30
31	use namespace tlf_internal;
32
33	[IMXMLObject]
34
35/**
36 * The text in a flow is stored in tree form with the elements of the tree representing logical
37 * divisions within the text. The FlowElement class is the abstract base class of all the objects in this tree.
38 * FlowElement objects represent paragraphs, spans of text within paragraphs, and
39 * groups of paragraphs.
40 *
41 * <p>The root of a composable FlowElement tree is always a TextFlow object. Leaf elements of the tree are always
42 * subclasses of the FlowLeafElement class. All leaves arranged in a composable TextFlow have a ParagraphElement ancestor.
43 * </p>
44 *
45 * <p>You cannot create a FlowElement object directly. Invoking <code>new FlowElement()</code> throws an error
46 * exception.</p>
47 *
48 * @playerversion Flash 10
49 * @playerversion AIR 1.5
50 * @langversion 3.0
51 *
52 * @see FlowGroupElement
53 * @see FlowLeafElement
54 * @see InlineGraphicElement
55 * @see ParagraphElement
56 * @see SpanElement
57 * @see TextFlow
58 */
59	public class FlowElement implements ITextLayoutFormat
60	{
61		/** every FlowElement has at most one parent */
62		private var _parent:FlowGroupElement;
63
64		/** format settings on this FlowElement. @private */
65		private var _format:FlowValueHolder;
66
67		/** @private computed formats applied to the element */
68		protected var _computedFormat:TextLayoutFormat;
69
70		/** start, _start of element, relative to parent; tlf_internal to eliminate getter calls  */
71		private var _parentRelativeStart:int = 0;
72
73		/** _textLength (number of chars) in the element, including child content; tlf_internal to eliminate getter calls */
74		private var _textLength:int = 0;
75
76		/** Base class - invoking <code>new FlowElement()</code> throws an error exception.
77		*
78		* @playerversion Flash 10
79		* @playerversion AIR 1.5
80	 	* @langversion 3.0
81	 	*/
82		public function FlowElement()
83		{
84			// not a valid FlowElement class
85			if (abstract)
86				throw new Error(GlobalSettings.resourceStringFunction("invalidFlowElementConstruct"));
87		}
88
89		/** Called for MXML objects after the implementing object has been created and all component properties specified on the MXML tag have been initialized.
90		 * @param document The MXML document that created the object.
91		 * @param id The identifier used by document to refer to this object.
92		 **/
93		public function initialized(document:Object, id:String):void
94		{
95			this.id = id;
96		}
97
98		/** Returns true if the class is an abstract base class,
99		 * false if its OK to construct. Attempting to instantiate an
100		 * abstract FlowElement class will cause an exception.
101		 * @return Boolean 	true if this is an abstract class
102		 * @private
103		 */
104		protected function get abstract():Boolean
105		{
106			return true;
107		}
108
109		/** Allows you to read and write user styles on a FlowElement object.  Note that reading this property
110		* makes a copy of the userStyles set in the format of this element.
111		*
112		* @playerversion Flash 10
113		* @playerversion AIR 1.5
114	 	* @langversion 3.0
115		*
116		*/
117		public function get userStyles():Object
118		{ return _format ? _format.userStyles : null; }
119		public function set userStyles(styles:Object):void
120		{
121			var val:String;
122			// clear the existing userstyles
123			for (val in userStyles)
124				this.setStyle(val,undefined);
125
126			// set the new ones
127			for (val in styles)
128			{
129				if (!TextLayoutFormat.description.hasOwnProperty(val))
130					this.setStyle(val,styles[val]);
131			}
132		}
133
134		/** Returns the <code>coreStyles</code> on this FlowElement.  Note that the getter makes a copy of the core
135		 * styles dictionary. The coreStyles object encapsulates the formats that are defined by TextLayoutFormat and are in TextLayoutFormat.description. The
136		 * <code>coreStyles</code> object consists of an array of <em>stylename-value</em> pairs.
137		 *
138		 * @see flashx.textLayout.formats.TextLayoutFormat
139		 *
140		 * @playerversion Flash 10
141		 * @playerversion AIR 1.5
142		 * @langversion 3.0
143		 */
144		public function get coreStyles():Object
145		{ return _format ? _format.coreStyles : null; }
146
147		/** Returns the styles on this FlowElement.  Note that the getter makes a copy of the
148		 * styles dictionary. The returned object encapsulates all styles set in the format property including core and user styles. The
149		 * returned object consists of an array of <em>stylename-value</em> pairs.
150		 *
151		 * @see flashx.textLayout.formats.TextLayoutFormat
152		 *
153		 * @playerversion Flash 10
154		 * @playerversion AIR 1.5
155		 * @langversion 3.0
156		 */
157		public function get styles():Object
158		{ return _format ? _format.styles : null; }
159
160		/** @private */
161		tlf_internal function setStylesInternal(styles:Object):void
162		{
163			if (styles)
164				writableTextLayoutFormat().setStyles(Property.shallowCopy(styles),false);
165			else if (_format)
166				_format.clearStyles();
167
168			formatChanged();
169		}
170
171		/** Compare the userStyles of this with otherElement's userStyles.
172		 *
173		 * @param otherElement the FlowElement object with which to compare user styles
174		 *
175		 * @return 	true if the user styles are equal; false otherwise.
176		 *
177		 * @playerversion Flash 10
178		 * @playerversion AIR 1.5
179		 * @langversion 3.0
180		 *
181	 	 * @includeExample examples\FlowElement_equalUserStylesExample.as -noswf
182	 	 *
183	 	 * @see #getStyle()
184	 	 * @see #setStyle()
185	 	 * @see #userStyles
186		 */
187		public function equalUserStyles(otherElement:FlowElement):Boolean
188		{
189			return Property.equalStyles(this.userStyles,otherElement.userStyles,null);
190		}
191
192		/** @private Compare the styles of two elements for merging.  Return true if they can be merged. */
193		tlf_internal function equalStylesForMerge(elem:FlowElement):Boolean
194		{
195			return this.id == elem.id && this.typeName == elem.typeName && TextLayoutFormat.isEqual(elem.format,format);
196		}
197
198		/**
199		 * Makes a copy of this FlowElement object, copying the content between two specified character positions.
200		 * It returns the copy as a new FlowElement object. Unlike <code>deepCopy()</code>, <code>shallowCopy()</code> does
201		 * not copy any of the children of this FlowElement object.
202		 *
203		 * <p>With no arguments, <code>shallowCopy()</code> defaults to copying all of the content.</p>
204		 *
205		 * @param relativeStart	The relative text position of the first character to copy. First position is 0.
206		 * @param relativeEnd	The relative text position of the last character to copy. A value of -1 indicates copy to end.
207		 *
208		 * @return 	the object created by the copy operation.
209		 *
210		 * @playerversion Flash 10
211		 * @playerversion AIR 1.5
212	 	 * @langversion 3.0
213	 	 *
214	 	 * @includeExample examples\FlowElement_shallowCopyExample.as -noswf
215		 *
216	 	 * @see #deepCopy()
217	 	 */
218
219		public function shallowCopy(relativeStart:int = 0, relativeEnd:int = -1):FlowElement
220		{
221			var retFlow:FlowElement = new (getDefinitionByName(getQualifiedClassName(this)) as Class);
222			if (_format !=  null)
223				retFlow._format = new FlowValueHolder(_format);
224
225			CONFIG::debug { assert(retFlow.styleName == styleName,"Failed top copy styleName"); }
226			CONFIG::debug { assert(retFlow.typeName == typeName,"Failed top copy typeName"); }
227			CONFIG::debug { assert(retFlow.id == id,"Failed top copy id"); }
228
229			return retFlow;
230		}
231
232		/**
233		 * Makes a deep copy of this FlowElement object, including any children, copying the content between the two specified
234		 * character positions and returning the copy as a FlowElement object.
235		 *
236		 * <p>With no arguments, <code>deepCopy()</code> defaults to copying the entire element.</p>
237		 *
238		 * @param relativeStart	relative text position of the first character to copy. First position is 0.
239		 * @param relativeEnd	relative text position of the last character to copy. A value of -1 indicates copy to end.
240		 *
241		 * @return 	the object created by the deep copy operation.
242		 * @playerversion Flash 10
243		 * @playerversion AIR 1.5
244	 	 * @langversion 3.0
245	 	 *
246	 	 * @includeExample examples\FlowElement_deepCopyExample.as -noswf
247	 	 *
248	 	 * @see #shallowCopy()
249		 */
250
251		public function deepCopy(relativeStart:int = 0, relativeEnd:int = -1):FlowElement
252		{
253			if (relativeEnd == -1)
254				relativeEnd = _textLength;
255
256			return shallowCopy(relativeStart, relativeEnd);
257		}
258
259		/**
260		 * Gets the specified range of text from a flow element.
261		 *
262		 * @param relativeStart The starting position of the range of text to be retrieved, relative to the start of the FlowElement
263		 * @param relativeEnd The ending position of the range of text to be retrieved, relative to the start of the FlowElement, -1 for up to the end of the element
264		 * @param paragraphSeparator character to put between paragraphs
265		 *
266		 * @return The requested text.
267		 *
268		 * @playerversion Flash 10
269		 * @playerversion AIR 1.5
270	 	 * @langversion 3.0
271		 */
272		public function getText(relativeStart:int=0, relativeEnd:int=-1, paragraphSeparator:String="\n"):String
273		{
274			return "";
275		}
276
277		/**
278		 * Splits this FlowElement object at the position specified by the <code>relativePosition</code> parameter, which is
279		 * a relative position in the text for this element. This method splits only SpanElement and FlowGroupElement
280		 * objects.
281		 *
282		 * @param relativePosition the position at which to split the content of the original object, with the first position being 0.
283		 *
284		 * @return	the new object, which contains the content of the original object, starting at the specified position.
285		 *
286		 * @throws RangeError if <code>relativePosition</code> is greater than <code>textLength</code>, or less than 0.
287		 *
288		 * @playerversion Flash 10
289		 * @playerversion AIR 1.5
290	 	 * @langversion 3.0
291		 */
292
293		public function splitAtPosition(relativePosition:int):FlowElement
294		{
295			if ((relativePosition < 0) || (relativePosition > _textLength))
296				throw RangeError(GlobalSettings.resourceStringFunction("invalidSplitAtPosition"));
297			return this;
298		}
299
300		/** @private Set to indicate the element may be "bound" in Flex - only used on FlowLeafElement and SubParagraphBlock elements. */
301		tlf_internal function get bindableElement():Boolean
302		{ return getPrivateStyle("bindable") == true; }
303
304		/** @private */
305		tlf_internal function set bindableElement(value:Boolean):void
306		{ setPrivateStyle("bindable", value); }
307
308
309		/** Merge flow element into previous flow element if possible. @private
310		 * @Return true--> did the merge
311		 */
312
313		tlf_internal function mergeToPreviousIfPossible():Boolean
314		{
315			return false;
316		}
317
318		/** @private
319		 * Create and initialize the FTE data structure that corresponds to the FlowElement
320		 */
321		tlf_internal function createContentElement():void
322		{
323			// overridden in the base class -- we should not get here
324			CONFIG::debug { assert(false,"invalid call to createContentElement"); }
325		}
326
327		/** @private
328		 * Release the FTE data structure that corresponds to the FlowElement, so it can be gc'ed
329		 */
330		tlf_internal function releaseContentElement():void
331		{
332			// overridden in the base class -- we should not get here
333			CONFIG::debug { assert(false,"invalid call to createContentElement"); }
334		}
335
336		/** Returns the parent of this FlowElement object. Every FlowElement has at most one parent.
337		*
338		* @playerversion Flash 10
339		* @playerversion AIR 1.5
340	 	* @langversion 3.0
341	 	*/
342
343		public function get parent():FlowGroupElement
344		{
345			// must be final to prevent overrides for safe internal access to _parent
346			return _parent;
347		}
348
349		/** @private parent setter. */
350
351		tlf_internal function setParentAndRelativeStart(newParent:FlowGroupElement,newStart:int):void
352		{ _parent = newParent; _parentRelativeStart = newStart; attributesChanged(false); }
353
354
355		/** @private Used when the textBlock.content is already correctly configured. */
356		tlf_internal function setParentAndRelativeStartOnly(newParent:FlowGroupElement,newStart:int):void
357		{ _parent = newParent;  _parentRelativeStart = newStart; }
358
359		/**
360		 * Returns the total length of text owned by this FlowElement object and its children.  If an element has no text, the
361		 * value of <code>textLength</code> is usually zero.
362		 *
363		 * <p>ParagraphElement objects have a final span with a paragraph terminator character for the last
364		 * SpanElement object.The paragraph terminator is included in the value of the <code>textLength</code> of that
365		 * SpanElement object and all its parents.  It is not included in <code>text</code> property of the SpanElement
366		 * object.</p>
367		 *
368		 * @playerversion Flash 10
369		 * @playerversion AIR 1.5
370	 	 * @langversion 3.0
371	 	 *
372	 	 * @see #textLength
373		 */
374
375		public function get textLength():int
376		{ return _textLength; }
377
378		/** @private textLength setter.  */
379		tlf_internal function setTextLength(newLength:int):void
380		{ _textLength = newLength; }
381
382		/** Returns the relative start of this FlowElement object in the parent. If parent is null, this value is always zero.
383		 * If parent is not null, the value is the sum of the text lengths of all previous siblings.
384		 *
385		 * @return offset
386		 *
387		 * @playerversion Flash 10
388		 * @playerversion AIR 1.5
389	 	 * @langversion 3.0
390	 	 *
391	 	 * @see #textLength
392		 */
393
394		public function get parentRelativeStart():int
395		{
396			// make final to prevent overrides and enable direct internal read access
397			return _parentRelativeStart;
398		}
399
400		/** @private start setter. */
401		tlf_internal function setParentRelativeStart(newStart:int):void
402		{ _parentRelativeStart = newStart; }
403
404		/** Returns the relative end of this FlowElement object in the parent. If the parent is null this is always equal to <code>textLength</code>.  If
405		 * the parent is not null, the value is the sum of the text lengths of this and all previous siblings, which is effectively
406		 * the first character in the next FlowElement object.
407		 *
408		 * @return offset
409		 *
410		 * @playerversion Flash 10
411		 * @playerversion AIR 1.5
412	 	 * @langversion 3.0
413	 	 *
414	 	 * @see FlowGroupElement
415	 	 * @see #textLength
416		 */
417
418		public function get parentRelativeEnd():int
419		{ return _parentRelativeStart + _textLength; }
420
421		/**
422		 * Tag for the item, used for debugging.
423		 */
424		CONFIG::debug public function get name():String
425		{
426			return flash.utils.getQualifiedClassName(this);
427		}
428
429		/** Returns the ContainerFormattedElement that specifies its containers for filling. This ContainerFormattedElement
430		 * object has its own columns and can control ColumnDirection and BlockProgression.
431		 *
432		 * @return 	the ancestor, with its container, of this FlowElement object.
433		 *
434		 * @playerversion Flash 10
435		 * @playerversion AIR 1.5
436	 	 * @langversion 3.0
437	 	 *
438	 	 * @private
439		 */
440
441		tlf_internal function getAncestorWithContainer():ContainerFormattedElement
442		{
443			var elem:FlowElement = this;
444			while (elem)
445			{
446				var contElement:ContainerFormattedElement = elem as ContainerFormattedElement;
447				if (contElement)
448				{
449					if (!contElement._parent || contElement.flowComposer)
450						return contElement;
451				}
452				elem = elem._parent;
453			}
454			return null;
455		}
456
457
458		/**
459		 * @private
460		 * Generic mechanism for fetching private data from the element.
461		 * @param styleName	name of the property
462		 */
463		tlf_internal function getPrivateStyle(styleName:String):*
464		{ return _format ? _format.getPrivateData(styleName) : undefined; }
465
466		/**
467		 * @private
468		 * Generic mechanism for adding private data to the element.
469		 * @param styleName	name of the property
470		 * @param val value of the property
471		 */
472		tlf_internal function setPrivateStyle(styleName:String, val:*):void
473		{
474			if (getPrivateStyle(styleName) != val)
475			{
476				writableTextLayoutFormat().setPrivateData(styleName,val);
477				modelChanged(ModelChange.STYLE_SELECTOR_CHANGED,this,0,this._textLength);
478			}
479		}
480
481		private static const idString:String ="id";
482
483		/**
484		 * Assigns an identifying name to the element, making it possible to set a style for the element
485		 * by referencing the <code>id</code>. For example, the following line sets the color for
486		 * a SpanElement object that has an id of span1:
487		 *
488		 * <listing version="3.0" >
489		 * textFlow.getElementByID("span1").setStyle("color", 0xFF0000);
490		 * </listing>
491		 *
492		 * @playerversion Flash 10
493		 * @playerversion AIR 1.5
494		 * @langversion 3.0
495		 *
496		 * @see TextFlow#getElementByID()
497		 */
498		public function get id():String
499		{ return getPrivateStyle(idString); }
500		public function set id(val:String):void
501		{ return setPrivateStyle(idString, val);	}
502
503		private static const typeNameString:String ="typeName";
504
505		/**
506		 * Each FlowElement has a <code>typeName</code>.  <code>typeName</code> defaults to the string the <code>textLayoutFormat</code> TextConverter uses.  This API
507		 * can be used to set a different <code>typeName</code> to a <code>FlowElement</code>.  Typically this is done to support <code>type</code> selectors in CSS.
508		 *
509		 * <p>See the <code>TEXT_FIELD_HTML_FORMAT</code> documentation for how this used..</p>
510		 *
511		 * @see flashx.textLayout.conversion.TextConverter
512		 * @see flashx.textLayout.conversion.TextConverter#TEXT_FIELD_HTML_FORMAT
513		 * @see flashx.textLayout.conversion.IHTMLImporter
514		 *
515		 * @playerversion Flash 10
516		 * @playerversion AIR 1.5
517		 * @langversion 3.0
518		 */
519		public function get typeName():String
520		{
521			var typeName:String = getPrivateStyle(typeNameString);
522			return typeName ? typeName : defaultTypeName;
523		}
524		public function set typeName(val:String):void
525		{
526			// extra testing here to avoid saving defaultTypeName in privateStyles
527			if (val != typeName)
528				setPrivateStyle(typeNameString, val == defaultTypeName ? undefined : val);
529		}
530
531		/** @private */
532		tlf_internal function get defaultTypeName():String
533		{ return null; }
534
535		private static const impliedElementString:String = "impliedElement";
536		/** @private */
537		tlf_internal function get impliedElement():Boolean
538		{
539			return getPrivateStyle(impliedElementString) !== undefined;
540		}
541		/** @private */
542		tlf_internal function set impliedElement(value:*):void
543		{
544			CONFIG::debug { assert(value === true || value === undefined,"Bad value to FlowElement.impliedElement"); }
545			setPrivateStyle(impliedElementString, value);
546		}
547
548		// ****************************************
549		// Begin TLFFormat Related code
550		// ****************************************
551		include "../formats/TextLayoutFormatInc.as"
552
553		/** TextLayoutFormat properties applied directly to this element.
554		 * <p>Each element may have properties applied to it as part of its format. Properties applied to this element override properties inherited from the parent. Properties applied to this element will in turn be inherited by element's children if they are not overridden on the child. If no properties are applied to the element, this will be null.</p>
555		 * @see flashx.textLayout.formats.ITextLayoutFormat
556		 */
557		public function get format():ITextLayoutFormat
558		{ return _format; }
559		public function set format(value:ITextLayoutFormat):void
560		{
561			if (value == _format)
562				return;
563
564			var oldStyleName:String = this.styleName;
565
566			if (value == null)
567				_format.clearStyles();
568			else
569				writableTextLayoutFormat().copy(value);
570			formatChanged();
571
572			if (oldStyleName != this.styleName)
573				styleSelectorChanged();
574		}
575
576		/** @private */
577		tlf_internal function writableTextLayoutFormat():FlowValueHolder
578		{
579			if (_format == null)
580				_format = new FlowValueHolder();
581			return _format;
582		}
583
584		/** This gets called when an element has changed its attributes. This may happen because an
585		 * ancestor element changed it attributes.
586		 * @private
587		 */
588		tlf_internal function formatChanged(notifyModelChanged:Boolean = true):void
589		{
590			if (notifyModelChanged)
591				modelChanged(ModelChange.TEXTLAYOUT_FORMAT_CHANGED,this,0,_textLength);
592			// require recompute of computedFormat
593			_computedFormat = null;
594		}
595
596		/** This gets called when an element has changed its style selection related attributes. This may happen because an
597		 * ancestor element changed it attributes.
598		 * @private
599		 */
600		tlf_internal function styleSelectorChanged():void
601		{
602			modelChanged(ModelChange.STYLE_SELECTOR_CHANGED,this,0,this._textLength);
603			_computedFormat = null;
604		}
605
606		/**
607		 * Concatenates tlf attributes with any other tlf attributes
608		 *
609		 * Return the concatenated result
610		 *
611		 * @private
612		 */
613		tlf_internal function get formatForCascade():ITextLayoutFormat
614		{
615			var tf:TextFlow = getTextFlow();
616			if (tf)
617			{
618				var elemStyle:TextLayoutFormat  = tf.getTextLayoutFormatStyle(this);
619				if (elemStyle)
620				{
621					var localFormat:ITextLayoutFormat = format;
622					if (localFormat == null)
623						return elemStyle;
624
625					var rslt:TextLayoutFormat = new TextLayoutFormat();
626					rslt.apply(elemStyle);
627					rslt.apply(localFormat);
628					return rslt;
629				}
630			}
631			return _format;
632		}
633
634		/** @private  Shared scratch element for use in computedFormat methods only */
635		static tlf_internal var _scratchTextLayoutFormat:TextLayoutFormat = new TextLayoutFormat();
636
637		/**
638		 * Returns the computed format attributes that are in effect for this element.
639		 * Takes into account the inheritance of attributes from parent elements.
640		 *
641		 * @playerversion Flash 10
642		 * @playerversion AIR 1.5
643	 	 * @langversion 3.0
644	 	 *
645		 * @see flashx.textLayout.formats.ITextLayoutFormat ITextLayoutFormat
646		 */
647
648		public function get computedFormat():ITextLayoutFormat
649		{
650			if (_computedFormat == null)
651				_computedFormat = doComputeTextLayoutFormat();
652			return _computedFormat;
653		}
654
655		/** @private */
656		tlf_internal function doComputeTextLayoutFormat():TextLayoutFormat
657		{
658			var parentPrototype:TextLayoutFormat = _parent ? TextLayoutFormat(_parent.computedFormat): null;
659			return FlowElement.createTextLayoutFormatPrototype(formatForCascade,parentPrototype);
660		}
661
662		// ****************************************
663		// End CharacterFormat Related code
664		// ****************************************
665
666
667		/** @private */
668		tlf_internal function attributesChanged(notifyModelChanged:Boolean = true):void
669		{
670			// TODO: REMOVE ME???
671			formatChanged(notifyModelChanged);
672		}
673
674		/** Returns the value of the style specified by the <code>styleProp</code> parameter, which specifies
675		 * the style name and can include any user style name. Accesses an existing span, paragraph, text flow,
676		 * or container style. Searches the parent tree if the style's value is <code>undefined</code> on a
677		 * particular element.
678		 *
679		 * @param styleProp The name of the style whose value is to be retrieved.
680		 *
681		 * @return The value of the specified style. The type varies depending on the type of the style being
682		 * accessed. Returns <code>undefined</code> if the style is not set.
683		 *
684		 * @playerversion Flash 10
685		 * @playerversion AIR 1.5
686	 	 * @langversion 3.0
687	 	 *
688	 	 * @includeExample examples\FlowElement_getStyleExample.as -noswf
689	 	 *
690	 	 * @see #clearStyle()
691		 * @see #setStyle()
692		 * @see #userStyles
693		 */
694		public function getStyle(styleProp:String):*
695		{
696			if (TextLayoutFormat.description.hasOwnProperty(styleProp))
697				return computedFormat.getStyle(styleProp);
698
699			var tf:TextFlow = getTextFlow();
700			if (!tf || !tf.formatResolver)
701				return computedFormat.getStyle(styleProp);
702
703			return getUserStyleWorker(styleProp);
704		}
705		// { return TextLayoutFormat.description.hasOwnProperty(styleProp) ? computedFormat.getStyle(styleProp) : getUserStyleWorker(styleProp); } }
706
707		/** @private worker function - any styleProp */
708		tlf_internal function getUserStyleWorker(styleProp:String):*
709		{
710			// CONFIG::debug { assert(TextLayoutFormat.description[styleProp] === undefined,"bad call to getUserStyleWorker"); }
711
712			if (_format != null)
713			{
714				var userStyle:* = _format.getStyle(styleProp)
715				if (userStyle !== undefined)
716					return userStyle;
717			}
718
719			var tf:TextFlow = getTextFlow();
720			if (tf && tf.formatResolver)
721			{
722				userStyle = tf.formatResolver.resolveUserFormat(this,styleProp);
723				if (userStyle !== undefined)
724					return userStyle;
725			}
726			// TODO: does TextFlow need to ask a "psuedo parent"
727			return _parent ? _parent.getUserStyleWorker(styleProp) : undefined;
728		}
729
730		/** Sets the style specified by the <code>styleProp</code> parameter to the value specified by the
731		* <code>newValue</code> parameter. You can set a span, paragraph, text flow, or container style, including
732		* any user name-value pair.
733		*
734		* <p><strong>Note:</strong> If you assign a custom style, Text Layout Framework can import and export it but
735		* compiled MXML cannot support it.</p>
736		*
737		* @param styleProp The name of the style to set.
738		* @param newValue The value to which to set the style.
739		*.
740		* @playerversion Flash 10
741		* @playerversion AIR 1.5
742		* @langversion 3.0
743		*
744		* @includeExample examples\FlowElement_setStyleExample.as -noswf
745		*
746		* @see #clearStyle()
747		* @see #getStyle()
748		* @see #userStyles
749		*/
750		public function setStyle(styleProp:String,newValue:*):void
751		{
752			if (TextLayoutFormat.description[styleProp])
753				this[styleProp] = newValue;
754			else
755			{
756				writableTextLayoutFormat().setStyle(styleProp,newValue);
757				formatChanged();
758			}
759		}
760
761		/** Clears the style specified by the <code>styleProp</code> parameter from this FlowElement object. Sets
762		 * the value to <code>undefined</code>.
763		 *
764		 * @param styleProp The name of the style to clear.
765
766		 * @playerversion Flash 10
767		 * @playerversion AIR 1.5
768		 * @langversion 3.0
769		 *
770		 * @includeExample examples\FlowElement_clearStyleExample.as -noswf
771		 *
772		 * @see #getStyle()
773		 * @see #setStyle()
774		 * @see #userStyles
775		 *
776		 */
777		public function clearStyle(styleProp:String):void
778		{ setStyle(styleProp,undefined); }
779
780		/**
781		 * Called whenever the model is modified.  Updates the TextFlow and notifies the selection manager - if it is set.
782		 * This method has to be called while the element is still in the flow
783		 * @param changeType - type of change
784		 * @param element - the actual element that is modified
785		 * @param start - elem relative offset of start of damage
786		 * @param len - length of damage
787		 * @see flow.model.ModelChange
788		 * @private
789		 */
790		tlf_internal function modelChanged(changeType:String, element:FlowElement, changeStart:int, changeLen:int, needNormalize:Boolean = true, bumpGeneration:Boolean = true):void
791		{
792			var tf:TextFlow = this.getTextFlow();
793			if (tf)
794				tf.processModelChanged(changeType, element, getAbsoluteStart()+changeStart, changeLen, needNormalize, bumpGeneration);
795		}
796
797		/** @private */
798		tlf_internal function appendElementsForDelayedUpdate(tf:TextFlow,changeType:String):void
799		{ }
800
801		/** @private */
802		tlf_internal function applyDelayedElementUpdate(textFlow:TextFlow,okToUnloadGraphics:Boolean,hasController:Boolean):void
803		{ }
804
805		/** @private */
806		tlf_internal function getEffectivePaddingLeft():Number
807		{ return computedFormat.paddingLeft == FormatValue.AUTO ? 0 : computedFormat.paddingLeft; }
808		/** @private */
809		tlf_internal function getEffectivePaddingRight():Number
810		{ return computedFormat.paddingRight == FormatValue.AUTO ? 0 : computedFormat.paddingRight; }
811		/** @private */
812		tlf_internal function getEffectivePaddingTop():Number
813		{ return computedFormat.paddingTop == FormatValue.AUTO ? 0 : computedFormat.paddingTop; }
814		/** @private */
815		tlf_internal function getEffectivePaddingBottom():Number
816		{ return computedFormat.paddingBottom == FormatValue.AUTO ? 0 : computedFormat.paddingBottom; }
817
818 		// ****************************************
819		// Begin tracking property Related code
820		// ****************************************
821
822		/**
823		 * Sets the tracking and is synonymous with the <code>trackingRight</code> property. Specified as a number of
824		 * pixels or a percent of <code>fontSize</code>.
825		 *
826		 * @playerversion Flash 10
827		 * @playerversion AIR 1.5
828	 	 * @langversion 3.0
829	 	 *
830	 	 * @see #trackingRight
831		 */
832		public function set tracking(trackingValue:Object):void
833		{
834			trackingRight = trackingValue;
835		}
836
837		// ****************************************
838		// End tracking property Related code
839		// ****************************************
840
841		// ****************************************
842		// Begin import helper code
843		// ****************************************
844
845		/** Strips white space from the element and its children, according to the whitespaceCollaspse
846		 *  value that has been applied to the element or inherited from its parent.
847		 *  If a FlowLeafElement's attrs are set to WhiteSpaceCollapse.PRESERVE, then collapse is
848		 *  skipped.
849		 *  @see text.formats.WhiteSpaceCollapse
850		 * @private
851		 */
852		tlf_internal function applyWhiteSpaceCollapse(collapse:String):void
853		{
854			// clear it if its set
855			if (whiteSpaceCollapse !== undefined)
856				whiteSpaceCollapse = undefined;
857
858			setPrivateStyle(impliedElementString, undefined);
859		}
860
861		// ****************************************
862		// End import helper code
863		// ****************************************
864
865		// ****************************************
866		// Begin tree navigation code
867		// ****************************************
868
869		 /**
870		 * Returns the start location of the element in the text flow as an absolute index. The first character in the flow
871		 * is position 0.
872		 *
873		 * @playerversion Flash 10
874		 * @playerversion AIR 1.5
875	 	 * @langversion 3.0
876		 *
877		 * @includeExample examples\FlowElement_getAbsoluteStartExample.as -noswf
878		 *
879 		 * @return The index of the start of the element from the start of the TextFlow object.
880 		 *
881 		 * @see #parentRelativeStart
882 		 * @see TextFlow
883	 	 */
884
885		public function getAbsoluteStart():int
886		{
887			var rslt:int = _parentRelativeStart;
888			for (var elem:FlowElement = _parent; elem; elem = elem._parent)
889				rslt += elem._parentRelativeStart;
890			return rslt;
891		}
892
893		/**
894		 * Returns the start of this element relative to an ancestor element. Assumes that the
895		 * ancestor element is in the parent chain. If the ancestor element is the parent, this is the
896		 * same as <code>this.parentRelativeStart</code>.  If the ancestor element is the grandparent, this is the same as
897		 * <code>parentRelativeStart</code> plus <code>parent.parentRelativeStart</code> and so on.
898		 *
899		 * @param ancestorElement The element from which you want to find the relative start of this element.
900		 *
901		 * @return  the offset of this element.
902		 *
903		 * @playerversion Flash 10
904		 * @playerversion AIR 1.5
905	 	 * @langversion 3.0
906	 	 *
907	 	 * @includeExample examples\FlowElement_getElementRelativeStartExample.as -noswf
908		 *
909		 * @see #getAbsoluteStart()
910		 */
911
912		public function getElementRelativeStart(ancestorElement:FlowElement):int
913		{
914			var rslt:int = _parentRelativeStart;
915			for (var elem:FlowElement = _parent; elem && elem != ancestorElement; elem = elem._parent)
916				rslt += elem._parentRelativeStart;
917			return rslt;
918		}
919
920		/**
921		 * Climbs the text flow hierarchy to return the root TextFlow object for the element.
922		 *
923		 * @return	The root TextFlow object for this FlowElement object.
924		 *
925		 * @playerversion Flash 10
926		 * @playerversion AIR 1.5
927	 	 * @langversion 3.0
928	 	 *
929	 	 * @includeExample examples\FlowElement_getTextFlowExample.as -noswf
930	 	 *
931	 	 * @see TextFlow
932		 */
933
934		public function getTextFlow():TextFlow
935		{
936			// find the root element entry and either return null or the containing textFlow
937			var elem:FlowElement = this;
938			while (elem._parent != null)
939				elem = elem._parent;
940			return elem as TextFlow;
941		}
942
943		/**
944		 * Returns the ParagraphElement object associated with this element. It looks up the text flow hierarchy and returns
945		 * the first ParagraphElement object.
946		 *
947		 * @return	the ParagraphElement object that's associated with this FlowElement object.
948		 *
949		 * @playerversion Flash 10
950		 * @playerversion AIR 1.5
951	 	 * @langversion 3.0
952	 	 *
953	 	 * @includeExample examples\FlowElement_getParagraphExample.as -noswf
954	 	 *
955	 	 * @see #getTextFlow()
956	 	 * @see ParagraphElement
957		 */
958
959		public function getParagraph():ParagraphElement
960		{
961			var para:ParagraphElement = null;
962			var rslt:FlowElement = this;
963			while (rslt)
964			{
965				para = rslt as ParagraphElement;
966				if (para)
967					break;
968				rslt = rslt._parent;
969			}
970			return para;
971		}
972
973
974		/**
975		 * Returns the FlowElement object that contains this FlowElement object, if this element is contained within
976		 * an element of a particular type.
977		 *
978		 * Returns the FlowElement it is contained in. Otherwise, it returns null.
979		 *
980		 * @private
981		 *
982		 * @param elementType	type of element for which to check
983		 */
984		public function getParentByType(elementType:Class):FlowElement
985		{
986			var curElement:FlowElement = _parent;
987			while (curElement)
988			{
989				if (curElement is elementType)
990					return curElement;
991				curElement = curElement._parent;
992			}
993			return null;
994		}
995
996		/** Returns the previous FlowElement sibling in the text flow hierarchy.
997		*
998		* @includeExample examples\FlowElement_getPreviousSiblingExample.as -noswf
999		*
1000		* @return 	the previous FlowElement object of the same type, or null if there is no previous sibling.
1001		*
1002		* @playerversion Flash 10
1003		* @playerversion AIR 1.5
1004	 	* @langversion 3.0
1005	 	*
1006	 	* @see #getNextSibling()
1007		*/
1008		public function getPreviousSibling():FlowElement
1009		{
1010			//this can happen if FE is on the scrap and doesn't have a parent. - gak 06.25.08
1011			if(!_parent)
1012				return null;
1013
1014			var idx:int = _parent.getChildIndex(this);
1015			return idx == 0 ? null : _parent.getChildAt(idx-1);
1016		}
1017
1018		/** Returns the next FlowElement sibling in the text flow hierarchy.
1019		*
1020		* @return the next FlowElement object of the same type, or null if there is no sibling.
1021		*
1022		* @includeExample examples\FlowElement_getNextSiblingExample.as -noswf
1023		*
1024		* @playerversion Flash 10
1025		* @playerversion AIR 1.5
1026	 	* @langversion 3.0
1027	 	*
1028	 	* @see #getPreviousSibling()
1029	 	*/
1030
1031		public function getNextSibling():FlowElement
1032		{
1033			if (!_parent)
1034				return null;
1035
1036			var idx:int = _parent.getChildIndex(this);
1037			return idx == _parent.numChildren-1 ? null : _parent.getChildAt(idx+1);
1038		}
1039
1040		/**
1041		* Returns the character at the specified position, relative to this FlowElement object. The first
1042		* character is at relative position 0.
1043		*
1044		* @param relativePosition	The relative position of the character in this FlowElement object.
1045		* @return String containing the character.
1046		*
1047		* @playerversion Flash 10
1048		* @playerversion AIR 1.5
1049	 	* @langversion 3.0
1050	 	*
1051	 	* @includeExample examples\FlowElement_getCharAtPositionExample.as -noswf
1052	 	*
1053	 	* @see #getCharCodeAtPosition()
1054	 	*/
1055
1056		public function getCharAtPosition(relativePosition:int):String
1057		{
1058			return null;
1059		}
1060
1061		/** Returns the character code at the specified position, relative to this FlowElement. The first
1062		* character is at relative position 0.
1063		*
1064		* @param relativePosition 	The relative position of the character in this FlowElement object.
1065		*
1066		* @return	the Unicode value for the character at the specified position.
1067		*
1068		* @playerversion Flash 10
1069		* @playerversion AIR 1.5
1070	 	* @langversion 3.0
1071	 	*
1072	 	* @includeExample examples\FlowElement_getCharCodeAtPositionExample.as -noswf
1073	 	*
1074	 	* @see #getCharAtPosition()
1075	 	*/
1076
1077		public function getCharCodeAtPosition(relativePosition:int):int
1078		{
1079			var str:String = getCharAtPosition(relativePosition);
1080			return str && str.length > 0 ? str.charCodeAt(0) : 0;
1081		}
1082
1083		/** @private apply function to all elements until it says stop */
1084		tlf_internal function applyFunctionToElements(func:Function):Boolean
1085		{ return func(this); }
1086
1087		/** @private
1088		 * Gets the EventDispatcher associated with this FlowElement.  Use the functions
1089		 * of EventDispatcher such as <code>setEventHandler()</code> and <code>removeEventHandler()</code>
1090		 * to capture events that happen over this FlowLeafElement object.  The
1091		 * event handler that you specify will be called after this FlowElement object does
1092		 * the processing it needs to do. If the FlowElement cannot dispatch events, the return
1093		 * value is null.
1094		 *
1095		 * Note that the event dispatcher will only dispatch FlowElementMouseEvent events.
1096		 *
1097		 * @playerversion Flash 10
1098		 * @playerversion AIR 1.5
1099		 * @langversion 3.0
1100		 *
1101		 * @see flash.events.EventDispatcher
1102		 * @see flashx.textLayout.events.FlowElementMouseEvent
1103		 */
1104		tlf_internal function getEventMirror():IEventDispatcher
1105		{
1106			return null;
1107		}
1108
1109		/** @private
1110		 * Checks whether an event dispatcher is attached, and if so, if the event dispatcher
1111		 * has any active listeners.
1112		 */
1113		tlf_internal function hasActiveEventMirror():Boolean
1114		{
1115			return false;
1116		}
1117
1118		// ****************************************
1119		// End tree navigation code
1120		// ****************************************
1121
1122		// ****************************************
1123		// Begin tree modification support code
1124		// ****************************************
1125
1126		/**
1127		 * Update the FlowElement to account for text added before it.
1128		 *
1129		 * @param len	number of characters added (may be negative if deletion)
1130		 */
1131		private function updateRange(len:int): void
1132		{
1133			setParentRelativeStart(_parentRelativeStart + len);
1134		}
1135
1136		/** Update the FlowElements in response to an insertion or deletion.
1137		 *  The length of the element inserted to is updated, and the length of
1138		 *  each of its ancestor element. Each of the elements following siblings
1139		 *  start index is updated (start index is relative to parent).
1140		 * @private
1141		 * @param startIdx		absolute index in flow where text was inserted
1142		 * @param len			number of characters added (negative if removed)
1143		 * updateLines			?? true if lines should be damaged??
1144		 * @private
1145		 */
1146		tlf_internal function updateLengths(startIdx:int,len:int,updateLines:Boolean):void
1147		{
1148			setTextLength(_textLength + len);
1149			var p:FlowGroupElement = _parent;
1150			if (p)
1151			{
1152				// update the elements following this in parent's children
1153				var idx:int = p.getChildIndex(this)+1;
1154				CONFIG::debug { assert(idx != -1,"bad parent in FlowElement.updateLengths"); }
1155
1156				var pElementCount:int = p.numChildren;
1157				while (idx < pElementCount)
1158				{
1159					var child:FlowElement = p.getChildAt(idx++);
1160					child.updateRange(len);
1161				}
1162
1163				// go up the tree to the root and update ancestor's lengths
1164				p.updateLengths(startIdx,len,updateLines);
1165			}
1166		}
1167
1168		/** @private */
1169		tlf_internal function getEnclosingController(relativePos:int) : ContainerController
1170		{
1171			// CONFIG::debug { assert(pos <= length,"getEnclosingController - bad pos"); }
1172
1173			var textFlow:TextFlow = getTextFlow();
1174
1175			//more than likely, we are building up spans for the very first time.
1176			//the container has not yet been created
1177			if (textFlow == null || textFlow.flowComposer == null)
1178				return null;
1179
1180			var curItem:FlowElement = this;
1181			while (curItem && (!(curItem is ContainerFormattedElement) || ContainerFormattedElement(curItem).flowComposer == null))
1182			{
1183				curItem = curItem._parent;
1184			}
1185
1186			var flowComposer:IFlowComposer = ContainerFormattedElement(curItem).flowComposer;
1187			if (!flowComposer)
1188				return null;
1189
1190			var controllerIndex:int = ContainerFormattedElement(curItem).flowComposer.findControllerIndexAtPosition(getAbsoluteStart() + relativePos,false);
1191			return controllerIndex != -1 ? flowComposer.getControllerAt(controllerIndex) : null;
1192		}
1193
1194
1195		/** @private */
1196		tlf_internal function deleteContainerText(endPos:int,deleteTotal:int):void
1197		{
1198			// update container counts
1199
1200			if (getTextFlow())		// update container lengths only for elements that are attached to the rootElement
1201			{
1202				var absoluteEndPos:int = getAbsoluteStart() + endPos;
1203				var absStartIdx:int = absoluteEndPos-deleteTotal;
1204
1205				while (deleteTotal > 0)
1206				{
1207					var charsDeletedFromCurContainer:int;
1208					var enclosingController:ContainerController = getEnclosingController(endPos-1);
1209					if (!enclosingController)
1210					{
1211						// The end of the deleted text may be overset, so it doesn't appear in a container.
1212						// Find the last container that contains the deleted text.
1213						enclosingController = getEnclosingController(endPos - deleteTotal);
1214						if (enclosingController)
1215						{
1216							var flowComposer:IFlowComposer = enclosingController.flowComposer;
1217							var myIdx:int = flowComposer.getControllerIndex(enclosingController);
1218							var previousEnclosingWithContent:ContainerController = enclosingController;
1219							CONFIG::debug { assert(myIdx >= 0,"FlowElement.deleteContainerText: bad return from getContainerControllerIndex"); }
1220							while (myIdx+1 < flowComposer.numControllers && enclosingController.absoluteStart + enclosingController.textLength < endPos)
1221							{
1222								enclosingController = flowComposer.getControllerAt(myIdx+1);
1223								if (enclosingController.textLength)
1224								{
1225									previousEnclosingWithContent = enclosingController;
1226									break;
1227								}
1228								myIdx++;
1229							}
1230						}
1231						if (!enclosingController || !enclosingController.textLength)
1232							enclosingController = previousEnclosingWithContent;
1233						if (!enclosingController)
1234							break;
1235					}
1236					var enclosingControllerBeginningPos:int = enclosingController.absoluteStart;
1237					if (absStartIdx < enclosingControllerBeginningPos)
1238					{
1239						charsDeletedFromCurContainer = absoluteEndPos - enclosingControllerBeginningPos + 1;
1240					}
1241					else if (absStartIdx < enclosingControllerBeginningPos + enclosingController.textLength)
1242					{
1243						charsDeletedFromCurContainer = deleteTotal;
1244					}
1245					// Container textFlowLength may contain fewer characters than the those to be deleted in case of overset text.
1246					var containerTextLengthDelta:int = enclosingController.textLength < charsDeletedFromCurContainer ? enclosingController.textLength : charsDeletedFromCurContainer;
1247					if (containerTextLengthDelta <= 0)
1248						break;		// overset text is not in the last container -- we've deleted the container count, so exit
1249					// working backwards - can't call setTextLength as it examines previous controllers and gets confused in the composeCompleteRatio logic
1250					ContainerController(enclosingController).setTextLengthOnly(enclosingController.textLength -  containerTextLengthDelta);
1251					deleteTotal -= containerTextLengthDelta;
1252					absoluteEndPos -= containerTextLengthDelta;
1253					endPos -= containerTextLengthDelta;
1254				}
1255			}
1256		}
1257
1258		/** @private */
1259		tlf_internal function normalizeRange(normalizeStart:uint,normalizeEnd:uint):void
1260		{
1261			// default is do nothing
1262		}
1263
1264		/** Support for splitting FlowLeafElements.  Does a quick copy of _characterFormat if necessary.  @private */
1265		tlf_internal function quickCloneTextLayoutFormat(sibling:FlowElement):void
1266		{
1267			_format = sibling._format ? new FlowValueHolder(sibling._format) : null;
1268			_computedFormat = null;
1269		}
1270
1271		// ****************************************
1272		// End tree modification support code
1273		// ****************************************
1274
1275		/** @private This API supports the inputmanager */
1276		tlf_internal function updateForMustUseComposer(textFlow:TextFlow):Boolean
1277		{ return false; }
1278
1279		// ****************************************
1280		// Begin debug support code
1281		// ****************************************
1282
1283		CONFIG::debug static private var debugDictionary:Dictionary = new Dictionary(true);
1284		CONFIG::debug static private var nextKey:int = 1;
1285
1286		CONFIG::debug static public function getDebugIdentity(o:Object):String
1287		{
1288			if (debugDictionary[o] == null)
1289			{
1290				var s:String = flash.utils.getQualifiedClassName(o);
1291				if (s)
1292					s = s.substr( s.lastIndexOf(":")+1);
1293				else
1294					s = "";
1295				debugDictionary[o] = s + "." + nextKey.toString();
1296				nextKey++;
1297			}
1298			return debugDictionary[o];
1299		}
1300
1301		/**
1302		 * Check FlowElement for internal consistency.
1303		 * @private
1304		 * Lots of internal checks are done on FlowElements, some are dependent
1305		 * on the type of element.
1306		 * @return Number of errors found
1307		 */
1308		CONFIG::debug public function debugCheckFlowElement(depth:int = 0, extraData:String = ""):int
1309		{
1310			if (Debugging.verbose)
1311			{
1312				if (depth == 0)
1313					trace("----------------------------------");
1314
1315				trace("FlowElement:",depth.toString(),getDebugIdentity(this),"start:",_parentRelativeStart.toString(),"length:",_textLength.toString(),extraData);
1316			}
1317			return 0;
1318		}
1319
1320		CONFIG::debug public static function elementPath(element:FlowElement):String
1321		{
1322			var result:String;
1323
1324			if (element != null)
1325			{
1326				if (element._parent != null)
1327					result = elementPath(element._parent) + "." + element.name + "[" + element._parent.getChildIndex(element) + "]";
1328				else
1329					result = element.name;
1330			}
1331			return result;
1332		}
1333
1334		/**
1335		 * debugging only - show element as string
1336		 */
1337		CONFIG::debug public function toString():String
1338		{
1339			return "flowElement:" + name + " start:" + _parentRelativeStart.toString() + " absStart:" + this.getAbsoluteStart().toString() + " textLength:" + _textLength.toString();
1340		}
1341		// ****************************************
1342		// End debug support code
1343		// ****************************************
1344
1345		// //////////////////////////////////////////////////////////////////////
1346		// BEGIN PROTOTYPING CASCADE SUPPORT
1347		// //////////////////////////////////////////////////////////////////////
1348
1349
1350
1351		/** @private */
1352		tlf_internal static function createTextLayoutFormatPrototype(localStyles:ITextLayoutFormat,parentPrototype:TextLayoutFormat):TextLayoutFormat
1353		{
1354			var parentPrototypeUsable:Boolean = true;
1355			var hasStylesSet:Boolean = false;
1356			// the actual prototype to use
1357			var parentStylesPrototype:Object;
1358			// create a new stylesObject with a parentPrototype
1359			if (parentPrototype)
1360			{
1361				parentStylesPrototype = parentPrototype.getStyles();
1362				if (parentStylesPrototype.hasNonInheritedStyles !== undefined)
1363				{
1364					// its either a boolean or if its been used once an object that has the non-inheriting styles reset
1365					if (parentStylesPrototype.hasNonInheritedStyles === true)
1366					{
1367						// create a modified prototype and give it all default non-inherited values
1368						var noInheritParentStylesPrototype:Object = Property.createObjectWithPrototype(parentStylesPrototype);
1369						TextLayoutFormat.resetModifiedNoninheritedStyles(noInheritParentStylesPrototype);
1370						// now save it in the parent for reuse
1371						parentStylesPrototype.hasNonInheritedStyles = noInheritParentStylesPrototype;
1372						parentStylesPrototype = noInheritParentStylesPrototype;
1373					}
1374					else
1375					{
1376						parentStylesPrototype = parentStylesPrototype.hasNonInheritedStyles;
1377					}
1378					parentPrototypeUsable = false;	// can't use it
1379				}
1380			}
1381			else
1382			{
1383				parentPrototype = TextLayoutFormat.defaultFormat as TextLayoutFormat;
1384				parentStylesPrototype = parentPrototype.getStyles();
1385			}
1386
1387			var stylesObject:Object = Property.createObjectWithPrototype(parentStylesPrototype);
1388
1389			var key:String;
1390			var val:*;
1391			var prop:Property;
1392			// has nonInherited Styles that are *different* from the default
1393			var hasNonInheritedStyles:Boolean = false;
1394
1395			// two cases depending on how localStyles are supplied
1396			if (localStyles != null)
1397			{
1398				var lvh:TextLayoutFormat = localStyles as TextLayoutFormat;
1399				if (lvh)
1400				{
1401					var coreStyles:Object = lvh.getStyles();
1402
1403					for (key in coreStyles)
1404					{
1405						val = coreStyles[key];
1406						if (val == FormatValue.INHERIT)
1407						{
1408							if (parentPrototype)
1409							{
1410								prop = TextLayoutFormat.description[key];
1411								if (prop && !prop.inherited)
1412								{
1413									// actually do the inheritance - might have been wiped out above!
1414									val = parentPrototype[key];
1415									if (stylesObject[key] != val)
1416									{
1417										stylesObject[key] = val;
1418										hasNonInheritedStyles = true;
1419										hasStylesSet = true;
1420										// CONFIG::debug { assert(val != prop.defaultValue,"Unexpected non-inheritance"); }
1421									}
1422								}
1423							}
1424						}
1425						else
1426						{
1427							if (stylesObject[key] != val)
1428							{
1429								prop = TextLayoutFormat.description[key];
1430								if (prop && !prop.inherited)
1431								{
1432									// CONFIG::debug { assert(val != prop.defaultValue,"Unexpected non-inheritance"); }
1433									hasNonInheritedStyles = true;
1434								}
1435								stylesObject[key] = val;
1436								hasStylesSet = true;	// doesn't matter if inherited or not
1437							}
1438						}
1439					}
1440				}
1441				else
1442				{
1443					for each (prop in TextLayoutFormat.description)
1444					{
1445						key = prop.name;
1446						val = localStyles[key];
1447						if (val !== undefined)
1448						{
1449							if (val == FormatValue.INHERIT)
1450							{
1451								if (parentPrototype)
1452								{
1453									if (!prop.inherited)
1454									{
1455										// actually do the inheritance - might have been wiped out above!
1456										val = parentPrototype[key];
1457										if (stylesObject[key] != val)
1458										{
1459											stylesObject[key] = val;
1460											hasNonInheritedStyles = true;
1461											hasStylesSet = true;
1462											// CONFIG::debug { assert(val != prop.defaultValue,"Unexpected non-inheritance"); }
1463										}
1464									}
1465								}
1466							}
1467							else
1468							{
1469								if (stylesObject[key] != val)
1470								{
1471									if (!prop.inherited)
1472									{
1473										// CONFIG::debug { assert(val != prop.defaultValue,"Unexpected non-inheritance"); }
1474										hasNonInheritedStyles = true;
1475									}
1476									stylesObject[key] = val;
1477									hasStylesSet = true;	// doesn't matter if inherited or not
1478								}
1479							}
1480						}
1481					}
1482				}
1483			}
1484
1485			var rslt:TextLayoutFormat;
1486
1487			if (!hasStylesSet)
1488			{
1489				// nothing has changed from the parent so just reuse it
1490				CONFIG::debug { assert(hasNonInheritedStyles == false,"stylesCount mismatch with hasNonInheritedStyles"); }
1491				if (parentPrototypeUsable)
1492					return parentPrototype;
1493				// we can use the parentStylesPrototype but not the parentPrototype
1494				rslt = new TextLayoutFormat();
1495				rslt.setStyles(stylesObject,true);
1496				return rslt;
1497			}
1498
1499			if (hasNonInheritedStyles)
1500			{
1501				// if its not identical in stylesObject need to set it
1502				CONFIG::debug { assert(stylesObject.hasNonInheritedStyles !== hasNonInheritedStyles,"unexpected nonInheritedStyles"); }
1503				stylesObject.hasNonInheritedStyles = true;
1504				stylesObject.setPropertyIsEnumerable("hasNonInheritedStyles",false);
1505			}
1506			else if (stylesObject.hasNonInheritedStyles !== undefined)
1507			{
1508				stylesObject.hasNonInheritedStyles = undefined;
1509				stylesObject.setPropertyIsEnumerable("hasNonInheritedStyles",false);
1510			}
1511
1512			rslt = new TextLayoutFormat();
1513			rslt.setStyles(stylesObject,false);
1514			return rslt;
1515		}
1516	}
1517
1518}
1519