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