| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You under the Apache License, Version 2.0 |
| // (the "License"); you may not use this file except in compliance with |
| // the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| package flashx.textLayout.elements |
| { |
| import flash.display.DisplayObject; |
| import flash.display.DisplayObjectContainer; |
| import flash.display.Loader; |
| import flash.display.LoaderInfo; |
| import flash.display.MovieClip; |
| import flash.display.Shape; |
| import flash.display.Sprite; |
| import flash.events.ErrorEvent; |
| import flash.events.Event; |
| import flash.events.IOErrorEvent; |
| import flash.geom.Rectangle; |
| import flash.net.URLRequest; |
| import flash.system.Capabilities; |
| import flash.text.engine.ContentElement; |
| import flash.text.engine.ElementFormat; |
| import flash.text.engine.FontMetrics; |
| import flash.text.engine.GraphicElement; |
| import flash.text.engine.TextBaseline; |
| import flash.text.engine.TextLine; |
| import flash.text.engine.TextRotation; |
| |
| import flashx.textLayout.compose.IFlowComposer; |
| import flashx.textLayout.compose.ISWFContext; |
| import flashx.textLayout.compose.TextFlowLine; |
| import flashx.textLayout.debug.Debugging; |
| import flashx.textLayout.debug.assert; |
| import flashx.textLayout.events.ModelChange; |
| import flashx.textLayout.events.StatusChangeEvent; |
| import flashx.textLayout.formats.BlockProgression; |
| import flashx.textLayout.formats.Float; |
| import flashx.textLayout.formats.FormatValue; |
| import flashx.textLayout.formats.ITextLayoutFormat; |
| import flashx.textLayout.formats.JustificationRule; |
| import flashx.textLayout.formats.TextLayoutFormat; |
| import flashx.textLayout.property.Property; |
| import flashx.textLayout.tlf_internal; |
| |
| use namespace tlf_internal; |
| |
| /** The InlineGraphicElement class handles graphic objects that display inline in the text. |
| * |
| * <p>You can embed a graphic or any DisplayObject or specify a URl for the location of the graphic. |
| * The <code>height</code> and <code>width</code> properties of InlineGraphicElement control the actual size |
| * of the graphic to display. These values also control how much space to allocate |
| * for the graphic in the TextLine object that contains the graphic. |
| * The <code>height</code> and <code>width</code> properties each can be one of:</p> |
| * <ol> |
| * <li>A number of pixels</li> |
| * <li>A percent of the measured size of the image</li> |
| * <li>The constant, "auto", which computes the size (Default value)</li> |
| * </ol> |
| * There are three properties, or accessors, pertaining to the width and height of a graphic: |
| * <ul> |
| * <li>The <code>width</code> and <code>height</code> properties</li> |
| * <li>The <code>measuredWidth</code> and <code>measuredHeight</code> properties, which are the width or height of the graphic at load time</li> |
| * <li>The <code>actualWidth</code> and <code>actualHeight</code> properties, which are the actual display and compose width and height of the graphic as computed from <code>width</code> or <code>height</code> and <code>measuredWidth</code> or <code>measuredHeight</code></li> |
| * </ul> |
| * <p>The values of the <code>actualWidth</code> and <code>actualHeight</code> properties are always zero until the graphic |
| * is loaded.</p> |
| * |
| * <p>If <code>source</code> is specified as a URI, the graphic is loaded asynchronously. If it's a DisplayObject, TextLayout uses the <code>width</code> and |
| * <code>height</code> at the time the graphic is set into the InlineGraphicElement object as <code>measuredHeight</code> and <code>measuredWidth</code>; |
| * its width and height are read immediately.</p> |
| * <p><strong>Notes</strong>: For graphics that are loaded asynchronously the user must listen for a |
| * StatusChangeEvent.INLINE_GRAPHIC_STATUS_CHANGE event on the TextFlow and call <code>IFlowComposer.updateAllControllers()</code> to have the |
| * graphic appear. The value of <code>measuredWidth</code> and <code>measuredHeight</code> for graphics that are in the |
| * process of loading is zero.</p> |
| * |
| * <p>Some inline graphics are animations or videos that possibly have audio. They begin to run the first time they are composed after they finish loading. |
| * They don't stop running until the flowComposer on the TextFlow is set to null. At that time they are stopped and unloaded.</p> |
| * |
| * The following restrictions apply to InLineGraphicElement objects: |
| * <ol> |
| * <li>On export of TLFMarkup, source is converted to a string. If the graphic element is |
| * a class, the Text Layout Framework can't export it properly</li>. |
| * <li>When doing a copy/paste operation of an InlineGraphicElement, if source can't be |
| * used to create a new InLineGraphicElement, it won't be pasted. For example if |
| * source is a DisplayObject, or if the graphic is set directly, it can't be |
| * duplicated. Best results are obtained if the source is the class of an embedded graphic |
| * though that doesn't export/import.</li> |
| * <li>InLineGraphicElement objects work in the factory (TextFlowTextLineFactory) only if |
| * the source is a class or if you explicitly set the graphic to a loaded graphic. |
| * InlineGraphic objects that require delayed loads generally do not show up.</li> |
| * </ol> |
| * @includeExample examples\InlineGraphicElementExample.as -noswf |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| * @see #actualHeight |
| * @see #actualWidth |
| * @see flash.display.DisplayObject DisplayObject |
| * @see flashx.textLayout.compose.IFlowComposer#updateAllControllers() |
| * @see flashx.textLayout.events.StatusChangeEvent StatusChangeEvent |
| * @see TextFlow |
| */ |
| public final class InlineGraphicElement extends FlowLeafElement |
| { |
| private var _source:Object; |
| |
| private var _graphic:DisplayObject; |
| private var _placeholderGraphic:Sprite; // a fake DisplayObject we put in the TextLine so it generates an atom |
| |
| private var _elementWidth:Number; |
| private var _elementHeight:Number; |
| |
| // internal status of the graphic. there are more status here than publicly shown |
| private var _graphicStatus:Object; |
| |
| // set when its ok - must delay until on the stage for dynamically loaded images |
| private var okToUpdateHeightAndWidth:Boolean; |
| |
| private var _width:*; |
| private var _height:*; |
| |
| // stash away the actual width and height of the graphic |
| private var _measuredWidth:Number; |
| private var _measuredHeight:Number; |
| |
| private var _float:*; |
| |
| static private const graphicElementText:String = String.fromCharCode(ContentElement.GRAPHIC_ELEMENT); |
| |
| /** Constructor - create new InlineGraphicElement object |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| public function InlineGraphicElement() |
| { |
| super(); |
| // The width/height on the FE.fe don't actually take until the InlineGraphicElement is loaded. |
| okToUpdateHeightAndWidth = false; |
| _measuredWidth = 0; |
| _measuredHeight = 0; |
| internalSetWidth(undefined); |
| internalSetHeight(undefined); |
| _graphicStatus = InlineGraphicElementStatus.LOAD_PENDING; |
| setTextLength(1); |
| _text = graphicElementText; // echo text property we get from FTE |
| } |
| |
| /** @private */ |
| override tlf_internal function createContentElement():void |
| { |
| if (_blockElement) |
| return; |
| |
| computedFormat; // BEFORE creating the element |
| var graphicElement:GraphicElement = new GraphicElement(); |
| _blockElement = graphicElement; |
| CONFIG::debug { Debugging.traceFTECall(_blockElement,null,"new GraphicElement()"); } |
| |
| updateContentElement(); |
| |
| super.createContentElement(); |
| } |
| |
| private function updateContentElement():void |
| { |
| var graphicElement:GraphicElement = _blockElement as GraphicElement; |
| // Setting textRotation throws if any of the parent GroupElements have textRotation other than ROTATE_0 |
| CONFIG::debug { assert(_blockElement.textRotation == TextRotation.ROTATE_0,"invalid text Rotation in ILG"); } |
| // CONFIG::debug { Debugging.traceFTEAssign(_blockElement,"textRotation",String(rotationPropertyDefinition.defaultValue)); } |
| |
| //we need to keep a place holder in the ge.graphic in order to be able to navigate |
| //to the element. Without it, the FTE model will remove the atom and selection will not be |
| //possible. - gak 12.12.08 |
| if (!_placeholderGraphic) |
| _placeholderGraphic = new Sprite(); |
| graphicElement.graphic = _placeholderGraphic; |
| |
| if (effectiveFloat != Float.NONE) |
| { |
| if (graphicElement.elementHeight != 0) |
| graphicElement.elementHeight = 0 ; |
| if (graphicElement.elementWidth != 0) |
| graphicElement.elementWidth = 0; |
| } |
| else |
| { |
| var height:Number = elementHeightWithMarginsAndPadding(); |
| if (graphicElement.elementHeight != height) |
| graphicElement.elementHeight = height; |
| var width:Number = elementWidthWithMarginsAndPadding(); |
| if (graphicElement.elementWidth != width) |
| graphicElement.elementWidth = width; |
| } |
| CONFIG::debug { Debugging.traceFTEAssign(_blockElement,"elementHeight",graphicElement.elementHeight); } |
| CONFIG::debug { Debugging.traceFTEAssign(_blockElement,"elementWidth",graphicElement.elementWidth); } |
| CONFIG::debug { Debugging.traceFTEAssign(graphicElement,"graphic",graphic); } // needs float fix |
| } |
| |
| // Recalculate graphicElement width & height after a format change |
| /** @private */ |
| public override function get computedFormat():ITextLayoutFormat |
| { |
| var updateGraphicElement:Boolean = _computedFormat == null; |
| super.computedFormat; |
| if (updateGraphicElement && _blockElement) |
| updateContentElement(); |
| |
| return _computedFormat; |
| } |
| |
| /** @private */ |
| tlf_internal function elementWidthWithMarginsAndPadding():Number |
| { |
| // no textflow is no padding |
| var textFlow:TextFlow = getTextFlow(); |
| if (!textFlow) |
| return elementWidth; |
| var paddingAmount:Number = textFlow.computedFormat.blockProgression == BlockProgression.RL ? |
| getEffectivePaddingTop() + getEffectivePaddingBottom() : |
| getEffectivePaddingLeft() + getEffectivePaddingRight(); |
| return elementWidth + paddingAmount; |
| } |
| |
| /** @private */ |
| tlf_internal function elementHeightWithMarginsAndPadding():Number |
| { |
| // no textflow is no padding |
| var textFlow:TextFlow = getTextFlow(); |
| if (!textFlow) |
| return elementWidth; |
| var paddingAmount:Number = textFlow.computedFormat.blockProgression == BlockProgression.RL ? |
| getEffectivePaddingLeft() + getEffectivePaddingRight() : |
| getEffectivePaddingTop() + getEffectivePaddingBottom(); |
| return elementHeight + paddingAmount; |
| } |
| |
| // internal values for _graphicStatus. It can also be an error code. |
| /** load initiated */ |
| static private const LOAD_INITIATED:String = "loadInitiated"; |
| /** public status string for open event received status. @see flash.display.LoaderInfo.Events.open */ |
| static private const OPEN_RECEIVED:String = "openReceived"; |
| /** load complete received status. @see flash.display.LoaderInfo.Events.open */ |
| static private const LOAD_COMPLETE:String = "loadComplete"; |
| /** loaded from embed */ |
| static private const EMBED_LOADED:String = "embedLoaded"; |
| /** specified as a DisplayObject */ |
| static private const DISPLAY_OBJECT:String = "displayObject"; |
| /** null graphic */ |
| static private const NULL_GRAPHIC:String = "nullGraphic"; |
| |
| |
| private static var isMac:Boolean = (Capabilities.os.search("Mac OS") > -1); |
| |
| /** The embedded graphic. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| public function get graphic():DisplayObject |
| { |
| return _graphic; |
| } |
| private function setGraphic(value:DisplayObject):void |
| { |
| _graphic = value; |
| // I think this should do a model change. But it will break paste because when we paste we do a reimport, |
| // which will cause a delayed update, which will bump the generation number *after* the command. Which |
| // will cause undo of the command not to work. |
| // modelChanged(ModelChange.ELEMENT_MODIFIED,0,textLength); |
| } |
| |
| |
| /** @private */ |
| tlf_internal function get placeholderGraphic():Sprite |
| { |
| return _placeholderGraphic; |
| } |
| |
| /** Width used by composition for laying out text around the graphic. @private */ |
| tlf_internal function get elementWidth():Number |
| { |
| return _elementWidth; |
| } |
| /** Width used by composition for laying out text around the graphic. @private */ |
| tlf_internal function set elementWidth(value:Number):void |
| { |
| _elementWidth = value; |
| |
| if (_blockElement) |
| { |
| (_blockElement as GraphicElement).elementWidth = (effectiveFloat != Float.NONE) ? 0 : elementWidthWithMarginsAndPadding(); |
| CONFIG::debug { Debugging.traceFTEAssign(GraphicElement(_blockElement as GraphicElement),"elementWidth",GraphicElement(_blockElement).elementWidth); } |
| |
| } |
| |
| modelChanged(ModelChange.ELEMENT_MODIFIED,this,0,textLength,true,false); |
| } |
| |
| /** Height used by composition for laying out text around the graphic. @private */ |
| tlf_internal function get elementHeight():Number |
| { |
| return _elementHeight; |
| } |
| /** Height used by composition for laying out text around the graphic. @private */ |
| tlf_internal function set elementHeight(value:Number):void |
| { |
| _elementHeight = value; |
| |
| if (_blockElement) |
| { |
| (_blockElement as GraphicElement).elementHeight = (effectiveFloat != Float.NONE) ? 0 : elementHeightWithMarginsAndPadding(); |
| CONFIG::debug { Debugging.traceFTEAssign((_blockElement as GraphicElement),"elementHeight",GraphicElement(_blockElement).elementHeight); } |
| } |
| modelChanged(ModelChange.ELEMENT_MODIFIED,this,0,textLength,true,false); |
| } |
| |
| /** Definition of the height property @private */ |
| static tlf_internal const heightPropertyDefinition:Property = Property.NewNumberOrPercentOrEnumProperty("height", FormatValue.AUTO, false, null, 0, 32000, "0%", "1000000%", FormatValue.AUTO ); |
| |
| /** Definition of the width property @private */ |
| static tlf_internal const widthPropertyDefinition:Property = Property.NewNumberOrPercentOrEnumProperty("width", FormatValue.AUTO, false, null, 0, 32000, "0%", "1000000%", FormatValue.AUTO ); |
| |
| /** Disabled due to player bug. @private */ |
| static tlf_internal const rotationPropertyDefinition:Property = Property.NewEnumStringProperty("rotation", TextRotation.ROTATE_0, false, null, |
| TextRotation.ROTATE_0, TextRotation.ROTATE_90, TextRotation.ROTATE_180, TextRotation.ROTATE_270); |
| |
| /** Definition of the float property @private */ |
| static tlf_internal const floatPropertyDefinition:Property = Property.NewEnumStringProperty("float", Float.NONE, false, null, |
| Float.NONE, Float.LEFT, Float.RIGHT, Float.START, Float.END); |
| |
| /** The current status of the image. On each status change the owning TextFlow sends a StatusChangeEvent. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| * @return one of LOAD_PENDING, LOADING, SIZE_PENDING, READY, ERROR |
| * @see flashx.textLayout.elements.InlineGraphicElementStatus |
| * @see flashx.textLayout.events.StatusChangeEvent |
| */ |
| |
| public function get status():String |
| { |
| switch(_graphicStatus) |
| { |
| case LOAD_INITIATED: |
| case OPEN_RECEIVED: |
| return InlineGraphicElementStatus.LOADING; |
| case LOAD_COMPLETE: |
| case EMBED_LOADED: |
| case DISPLAY_OBJECT: |
| case NULL_GRAPHIC: |
| return InlineGraphicElementStatus.READY; |
| case InlineGraphicElementStatus.LOAD_PENDING: |
| case InlineGraphicElementStatus.SIZE_PENDING: |
| return String(_graphicStatus); |
| } |
| CONFIG::debug { assert(_graphicStatus is ErrorEvent,"unexpected _graphicStatus"); } |
| return InlineGraphicElementStatus.ERROR; |
| } |
| |
| private function changeGraphicStatus(stat:Object):void |
| { |
| var oldStatus:String = status; |
| _graphicStatus = stat; |
| var newStatus:String = status; |
| if (oldStatus != newStatus || stat is ErrorEvent) |
| { |
| var tf:TextFlow = getTextFlow(); |
| if (tf) |
| { |
| if (newStatus == InlineGraphicElementStatus.SIZE_PENDING) |
| tf.processAutoSizeImageLoaded(this); |
| tf.dispatchEvent(new StatusChangeEvent(StatusChangeEvent.INLINE_GRAPHIC_STATUS_CHANGE, false, false, this, newStatus, stat as ErrorEvent)); |
| } |
| } |
| } |
| |
| /** The width of the graphic. The value can be 'auto', a number of pixels or a percent of the measured width of the image. |
| * |
| * <p>Legal values are flashx.textLayout.formats.FormatValue.AUTO and flashx.textLayout.formats.FormatValue.INHERIT.</p> |
| * <p>Legal values as a number are from 0 to 32000.</p> |
| * <p>Legal values as a percent are numbers from 0 to 1000000.</p> |
| * <p>Default value is undefined indicating not set.</p> |
| * <p>If undefined or "inherit" the InlineGraphicElement will use the default value of "auto".</p> |
| * |
| * @throws RangeError when set value is not within range for this property |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| * @see #actualWidth |
| * @see #measuredWidth |
| */ |
| public function get width():* |
| { return _width; } |
| public function set width(w:*):void |
| { |
| internalSetWidth(w); |
| modelChanged(ModelChange.ELEMENT_MODIFIED,this,0,textLength); |
| } |
| |
| /** The natural width of the graphic. This is the width of the graphic at load time. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| * @see #actualWidth |
| * @see #width |
| */ |
| |
| public function get measuredWidth():Number |
| { return _measuredWidth; } |
| |
| /** The actual width in effect. This is the display and compose width that's computed from the |
| * <code>width</code> and <code>measuredWidth</code> properties. |
| * |
| * <p>The values of the <code>actualWidth</code>property are computed according to the |
| * following table:</p> |
| * <table class="innertable" width="100%"> |
| * <tr> |
| * <th>width property</th> |
| * <th>actualWidth</th> |
| * </tr> |
| * <tr> |
| * <td>auto</td> |
| * <td>measuredWidth</td> |
| * </tr> |
| * <tr> |
| * <td>w a Percent</td> |
| * <td>w percent of measuredWidth</td> |
| * </tr> |
| * <tr> |
| * <td>w a Number</td> |
| * <td>w</td> |
| * </tr> |
| * </table> |
| * |
| * <p><strong>Notes</strong>: If the inline graphic is a DisplayObject, its width and height are read immediately. |
| * If <code>measuredWidth</code> or <code>measuredHeight</code> are zero, then any auto calculations that would cause a divide by zero sets the result to zero.</p> |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| * @see #measuredWidth |
| * @see #width |
| * |
| */ |
| |
| public function get actualWidth():Number |
| { return elementWidth; } |
| |
| private function widthIsComputed():Boolean |
| { return internalWidth is String; } |
| |
| private function get internalWidth():Object |
| { return _width === undefined ? widthPropertyDefinition.defaultValue : _width; } |
| |
| private function computeWidth():Number |
| { |
| CONFIG::debug { assert(widthIsComputed(),"bad call to InlineGraphicElement.computeWidth"); } |
| if (internalWidth == FormatValue.AUTO) |
| { |
| if (internalHeight == FormatValue.AUTO) |
| return _measuredWidth; |
| if (_measuredHeight == 0 || _measuredWidth == 0) |
| return 0; |
| // can't rely on height being calculated yet |
| var effHeight:Number = heightIsComputed() ? computeHeight(): Number(internalHeight); |
| return effHeight/_measuredHeight * _measuredWidth; |
| } |
| return widthPropertyDefinition.computeActualPropertyValue(internalWidth,_measuredWidth); |
| } |
| |
| private function internalSetWidth(w:*):void |
| { |
| _width = widthPropertyDefinition.setHelper(width,w); |
| elementWidth = widthIsComputed() ? 0 : Number(internalWidth); |
| if (okToUpdateHeightAndWidth && graphic) |
| { |
| if (widthIsComputed()) |
| elementWidth = computeWidth(); |
| graphic.width = elementWidth; |
| CONFIG::debug { Debugging.traceFTEAssign(graphic,"width",elementWidth); } |
| if (internalHeight == FormatValue.AUTO) |
| { |
| elementHeight = computeHeight(); |
| graphic.height = elementHeight; |
| CONFIG::debug { Debugging.traceFTEAssign(graphic,"height",elementHeight); } |
| } |
| } |
| } |
| |
| /** The height of the image. May be 'auto', a number of pixels or a percent of the measured height. |
| * |
| * <p>Legal values are flashx.textLayout.formats.FormatValue.AUTO and flashx.textLayout.formats.FormatValue.INHERIT.</p> |
| * <p>Legal values as a number are from 0 to 32000.</p> |
| * <p>Legal values as a percent are numbers from 0 to 1000000.</p> |
| * <p>Default value is undefined indicating not set.</p> |
| * <p>If undefined or "inherit" the InlineGraphicElement will use the default value of "auto".</p> |
| * |
| * @throws RangeError when set value is not within range for this property |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| * @see #actualHeight |
| * @see #measuredHeight |
| */ |
| public function get height():* |
| { return _height; } |
| public function set height(h:*):void |
| { |
| internalSetHeight(h); |
| modelChanged(ModelChange.ELEMENT_MODIFIED,this,0,textLength); |
| } |
| |
| private function get internalHeight():Object |
| { return _height === undefined ? heightPropertyDefinition.defaultValue : _height; } |
| |
| /** @private */ |
| tlf_internal function get computedFloat():* |
| { |
| if (_float === undefined) |
| return floatPropertyDefinition.defaultValue; |
| return _float; |
| } |
| |
| private var _effectiveFloat:String; // how it was last composed |
| /** @private */ |
| tlf_internal function get effectiveFloat():* |
| { |
| return _effectiveFloat ? _effectiveFloat : computedFloat; |
| } |
| /** @private */ |
| tlf_internal function setEffectiveFloat(floatValue:String):void |
| { |
| if (_effectiveFloat != floatValue) |
| { |
| _effectiveFloat = floatValue; |
| if (_blockElement) |
| updateContentElement(); |
| } |
| } |
| |
| [Inspectable(enumeration="none,left,right,start,end")] |
| /** |
| * Controls the placement of the graphic relative to the text. It can be part of the line, or can be beside the line with the text |
| * wrapped around it. |
| * <p>Legal values are <code>flashx.textLayout.formats.Float.NONE</code>, <code>flashx.textLayout.formats.Float.LEFT</code>, |
| * <code>flashx.textLayout.formats.Float.RIGHT</code>, <code>flashx.textLayout.formats.Float.START</code>, and <code>flashx.textLayout.formats.Float.END</code>.</p> |
| * <p>Default value is undefined indicating not set.</p> |
| * <p>If undefined will be treated as <code>Float.NONE</code>.</p> |
| * |
| * @throws RangeError when set value is not within range for this property |
| * |
| * @see flashx.textLayout.formats.Float |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 2.0 |
| * @langversion 3.0 |
| */ |
| public function get float():* |
| { |
| return _float; |
| } |
| public function set float(value:*):* |
| { |
| value = floatPropertyDefinition.setHelper(float, value) as String; |
| if (_float != value) |
| { |
| _float = value; |
| if (_blockElement) |
| updateContentElement(); |
| modelChanged(ModelChange.ELEMENT_MODIFIED,this,0,textLength); |
| } |
| } |
| |
| /** The natural height of the graphic. This is the height of the graphic at load time. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| * @see #actualHeight |
| * @see #height |
| */ |
| |
| public function get measuredHeight():Number |
| { return _measuredHeight; } |
| |
| /** The actual height in effect. This is the display and compose height that's computed from the |
| * <code>height</code> and <code>measuredHeight</code> properties. |
| * |
| * <p>The values of the <code>actualHeight</code> property are computed according to the following table:</p> |
| * <table class="innertable" width="100%"> |
| * <tr> |
| * <th>height property</th> |
| * <th>actualHeight</th> |
| * </tr> |
| * <tr> |
| * <td>auto</td> |
| * <td>measuredheight</td> |
| * </tr> |
| * <tr> |
| * <td>h a Percent</td> |
| * <td>h percent of measuredheight</td> |
| * </tr> |
| * <tr> |
| * <td>h a Number</td> |
| * <td>h</td> |
| * </tr> |
| * </table> |
| * <p><strong>Notes</strong>: If the inline graphic is a DisplayObject, its width and height are read immmediately. |
| * If <code>measuredWidth</code> or <code>measuredHeight</code> are zero, then any auto calculations that would cause a divide by zero sets the result to zero.</p> |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| * @see #height |
| * @see #measuredHeight |
| */ |
| |
| public function get actualHeight():Number |
| { return elementHeight; } |
| |
| private function heightIsComputed():Boolean |
| { return internalHeight is String; } |
| |
| private function computeHeight():Number |
| { |
| CONFIG::debug { assert(heightIsComputed(),"bad call to InlineGraphicElement.computeWidth"); } |
| if (internalHeight == FormatValue.AUTO) |
| { |
| if (internalWidth == FormatValue.AUTO) |
| return _measuredHeight; |
| if (_measuredHeight == 0 || _measuredWidth == 0) |
| return 0; |
| // can't rely on width being calculated yet |
| var effWidth:Number = widthIsComputed() ? computeWidth(): Number(internalWidth); |
| return effWidth/_measuredWidth * _measuredHeight; |
| } |
| return heightPropertyDefinition.computeActualPropertyValue(internalHeight,_measuredHeight); |
| } |
| |
| private function internalSetHeight(h:*):void |
| { |
| _height = heightPropertyDefinition.setHelper(height,h); |
| elementHeight = heightIsComputed() ? 0 : Number(internalHeight); |
| if (okToUpdateHeightAndWidth && graphic != null) |
| { |
| if (heightIsComputed()) |
| elementHeight = computeHeight(); |
| graphic.height = elementHeight; |
| CONFIG::debug { Debugging.traceFTEAssign(graphic,"height",elementHeight); } |
| |
| if (internalWidth == FormatValue.AUTO) |
| { |
| elementWidth = computeWidth(); |
| graphic.width = elementWidth; |
| CONFIG::debug { Debugging.traceFTEAssign(graphic,"width",elementWidth); } |
| } |
| } |
| } |
| |
| private function loadCompleteHandler(e:Event):void |
| { |
| CONFIG::debug { Debugging.traceFTECall(null,null,"loadCompleteHandler",this); } |
| removeDefaultLoadHandlers(graphic as Loader); |
| CONFIG::debug { assert(okToUpdateHeightAndWidth == false,"invalid call to loadCompleteHandler"); } |
| okToUpdateHeightAndWidth = true; |
| |
| var g:DisplayObject = graphic; |
| _measuredWidth = g.width; |
| _measuredHeight = g.height; |
| |
| if (!widthIsComputed()) |
| g.width = elementWidth; |
| if (!heightIsComputed()) |
| g.height = elementHeight; |
| |
| if (e is IOErrorEvent) |
| changeGraphicStatus(e); |
| else if (widthIsComputed() || heightIsComputed()) |
| { |
| g.visible = false; |
| // triggers a delayedElementUpdate |
| changeGraphicStatus(InlineGraphicElementStatus.SIZE_PENDING); |
| } |
| else |
| changeGraphicStatus(LOAD_COMPLETE); |
| } |
| |
| private function openHandler(e:Event):void |
| { |
| changeGraphicStatus(OPEN_RECEIVED); |
| } |
| |
| private function addDefaultLoadHandlers(loader:Loader):void |
| { |
| var loaderInfo:LoaderInfo = loader.contentLoaderInfo; |
| CONFIG::debug { Debugging.traceFTECall(loaderInfo,loader,"contentLoaderInfo"); } |
| |
| loaderInfo.addEventListener(Event.OPEN, openHandler, false, 0, true); |
| CONFIG::debug { Debugging.traceFTECall(null,loaderInfo,"addEventListener",Event.OPEN, "openHandler", false, 0, true); } |
| loaderInfo.addEventListener(Event.COMPLETE,loadCompleteHandler,false,0,true); |
| CONFIG::debug { Debugging.traceFTECall(null,loaderInfo,"addEventListener",Event.COMPLETE, "loadCompleteHandler", false, 0, true); } |
| loaderInfo.addEventListener(IOErrorEvent.IO_ERROR,loadCompleteHandler,false,0,true); |
| CONFIG::debug { Debugging.traceFTECall(null,loaderInfo,"addEventListener",IOErrorEvent.IO_ERROR, "loadCompleteHandler", false, 0, true); } |
| } |
| |
| private function removeDefaultLoadHandlers(loader:Loader):void |
| { |
| CONFIG::debug{ assert(loader != null,"bad call to removeDefaultLoadHandlers"); } |
| loader.contentLoaderInfo.removeEventListener(Event.OPEN, openHandler); |
| loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loadCompleteHandler); |
| loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, loadCompleteHandler); |
| } |
| |
| /** Sets the source for the graphic. |
| * |
| * The value can be either a String that is interpreted as a URI, a Class that's interpreted as the class of an |
| * embeddded DisplayObject, a DisplayObject instance, or a URLRequest. Creates a DisplayObject and, |
| * if the InlineGraphicElement object is added into a ParagraphElement in a TextFlow object, causes it to appear |
| * inline in the text. |
| * |
| * @includeExample examples\InlineGraphicElement_sourceExample.as -noswf |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| public function get source():Object |
| { |
| return _source; |
| } |
| public function set source(value:Object):void |
| { |
| stop(true); |
| _source = value; |
| changeGraphicStatus(InlineGraphicElementStatus.LOAD_PENDING); |
| modelChanged(ModelChange.ELEMENT_MODIFIED,this,0,textLength); |
| } |
| |
| /** @private This method starts and stops ILGs. Generally TLF delays starting an ILG until the first compose. It shuts them down when they |
| * are deleted from the TextFlow or the TextFlow has its flowComposer removed. Some cases are ambigious, for example removeAllControllers doesn't stop ILGs |
| * but the client may desire it too. In cases where ILGs don't get stopped or started there are two tlf_internal functions on TextFlow: |
| * unloadGraphics - unloads and stops all ILGs |
| * prepareGraphicsForLoad - puts all ILGs that are not loaded in a list so that they are loaded on the next compose. |
| * This function is also called when an auto-size or percentage sized graphic is loaded. In that case the same list as prepareGraphicsForLoad is used. |
| * */ |
| tlf_internal override function applyDelayedElementUpdate(textFlow:TextFlow,okToUnloadGraphics:Boolean,hasController:Boolean):void |
| { |
| if (textFlow != this.getTextFlow()) |
| hasController = false; |
| |
| CONFIG::debug { assert(textFlow != null,"ILG:applyDelayedElementUpdate: null textFlow"); } |
| if (_graphicStatus == InlineGraphicElementStatus.LOAD_PENDING) |
| { |
| // trace("FEX LOADING", this.toString()); |
| if (hasController) |
| { |
| var source:Object = _source; |
| if (source is String) |
| { |
| var inlineGraphicResolver:Function = textFlow.configuration.inlineGraphicResolverFunction; |
| if (inlineGraphicResolver != null) |
| source = inlineGraphicResolver(this); |
| } |
| |
| var elem:DisplayObject; |
| if (source is String || source is URLRequest) |
| { |
| okToUpdateHeightAndWidth = false; |
| var loader:Loader = new Loader(); |
| CONFIG::debug { Debugging.traceFTECall(loader,null,"new Loader()"); } |
| |
| // set the width/height on COMPLETE or IOError |
| try |
| { |
| addDefaultLoadHandlers(loader); |
| if (source is String) |
| { |
| var myPattern:RegExp = /\\/g; |
| var src:String = source as String; |
| src = src.replace(myPattern, "/"); |
| //workaround for Watson bug 1896186. FlashPlayer requres that file |
| //names be encoded on Macintosh, but not on Windows. Grouped this |
| //bug with FlashPlayer Watson bug 1899687 |
| var pictURLReq:URLRequest; |
| if (isMac) |
| { |
| pictURLReq = new URLRequest(encodeURI(src)); |
| CONFIG::debug { Debugging.traceFTECall(pictURLReq,null,"new URLRequest",encodeURI(src)); } |
| } |
| else |
| { |
| pictURLReq = new URLRequest(src); |
| CONFIG::debug { Debugging.traceFTECall(pictURLReq,null,"new URLRequest",src); } |
| } |
| |
| loader.load(pictURLReq); |
| CONFIG::debug { Debugging.traceFTECall(null,loader,"load",pictURLReq); } |
| } |
| else |
| loader.load(URLRequest(source)); |
| |
| setGraphic(loader); |
| changeGraphicStatus(LOAD_INITIATED); |
| } |
| catch(e:Error) |
| { |
| // the load didn't initiate |
| removeDefaultLoadHandlers(loader); |
| elem = new Shape(); |
| changeGraphicStatus(NULL_GRAPHIC); |
| } |
| } |
| else if (source is Class) // value is class --> it is an Embed |
| { |
| var cls:Class = source as Class; |
| elem = DisplayObject(new cls()); |
| changeGraphicStatus(EMBED_LOADED); |
| } |
| else if (source is DisplayObject) |
| { |
| elem = DisplayObject(source); |
| changeGraphicStatus(DISPLAY_OBJECT); |
| } |
| else |
| { |
| elem = new Shape(); |
| changeGraphicStatus(NULL_GRAPHIC); |
| } |
| |
| // complete setup of width and height |
| if (_graphicStatus != LOAD_INITIATED) |
| { |
| okToUpdateHeightAndWidth = true; |
| _measuredWidth = elem ? elem.width : 0; |
| _measuredHeight = elem ? elem.height : 0; |
| |
| if (widthIsComputed()) |
| { |
| if (elem) |
| { |
| elem.width = elementWidth = computeWidth(); |
| CONFIG::debug { Debugging.traceFTEAssign(elem,"width",elem.width); } |
| } |
| else |
| elementWidth = 0; |
| } |
| else |
| { |
| elem.width = Number(width); |
| CONFIG::debug { Debugging.traceFTEAssign(elem,"width",elem.width); } |
| } |
| |
| if (heightIsComputed()) |
| { |
| if (elem) |
| { |
| elem.height = elementHeight = computeHeight(); |
| CONFIG::debug { Debugging.traceFTEAssign(elem,"height",elem.height); } |
| } |
| else |
| elementHeight = 0; |
| } |
| else |
| { |
| elem.height = Number(height); |
| CONFIG::debug { Debugging.traceFTEAssign(elem,"height",elem.height); } |
| } |
| |
| setGraphic(elem); |
| } |
| } |
| } |
| else |
| { |
| if (_graphicStatus == InlineGraphicElementStatus.SIZE_PENDING) |
| { |
| // this is width/height auto case hasn't been set yet - the graphic is hidden! |
| updateAutoSizes(); |
| graphic.visible = true; |
| changeGraphicStatus(LOAD_COMPLETE); |
| } |
| if (!hasController) |
| { |
| // shutdown the audio on any movie clips |
| stop(okToUnloadGraphics); |
| } |
| } |
| } |
| |
| /** @private This API supports the inputmanager */ |
| tlf_internal override function updateForMustUseComposer(textFlow:TextFlow):Boolean |
| { |
| applyDelayedElementUpdate(textFlow,false,true); |
| return status != InlineGraphicElementStatus.READY; |
| } |
| |
| /** This function updates the size of the graphic element when the size is expressed as a percentage of the graphic's actual size. */ |
| private function updateAutoSizes():void |
| { |
| if (widthIsComputed()) |
| { |
| elementWidth = computeWidth(); |
| graphic.width = elementWidth; |
| } |
| if (heightIsComputed()) |
| { |
| elementHeight = computeHeight(); |
| graphic.height = elementHeight; |
| } |
| } |
| |
| |
| /** @private Stop this inlinegraphicelement - if its a movieclip will stop noise and playing - if a load is in process it cancels the load */ |
| tlf_internal function stop(okToUnloadGraphics:Boolean):Boolean |
| { |
| |
| // watch for changing the source while we've got an event listener on the current graphic |
| // if so cancel the load and remove the listeners |
| if (_graphicStatus == OPEN_RECEIVED || _graphicStatus == LOAD_INITIATED) |
| { |
| var loader:Loader = graphic as Loader; |
| try |
| { |
| loader.close(); // cancels in process load |
| } |
| catch (e:Error) |
| { /* ignore */ } |
| removeDefaultLoadHandlers(loader); |
| } |
| |
| // shutdown any running movieclips - this graphic will no longer be referenced |
| // for graphics that the client has passed us - just ignore they own the responsibliity |
| if (_graphicStatus != DISPLAY_OBJECT) |
| { |
| if (okToUnloadGraphics) |
| { |
| recursiveShutDownGraphic(graphic); |
| setGraphic(null); |
| } |
| if (widthIsComputed()) |
| elementWidth = 0; |
| if (heightIsComputed()) |
| elementHeight = 0; |
| changeGraphicStatus(InlineGraphicElementStatus.LOAD_PENDING); |
| } |
| return true; |
| } |
| |
| // searches through the graphic and stops any playing grpahics |
| private static function recursiveShutDownGraphic(graphic:DisplayObject):void |
| { |
| if (graphic is Loader) |
| Loader(graphic).unloadAndStop(); |
| else if (graphic) |
| { |
| var container:DisplayObjectContainer = graphic as DisplayObjectContainer; |
| if (container) |
| { |
| for (var idx:int = 0; idx < container.numChildren; idx++) |
| { |
| recursiveShutDownGraphic(container.getChildAt(idx)); |
| } |
| } |
| |
| if (graphic is MovieClip) |
| MovieClip(graphic).stop(); |
| } |
| } |
| |
| /** @private */ |
| tlf_internal override function getEffectiveFontSize():Number |
| { |
| if (effectiveFloat != Float.NONE) |
| return 0; |
| var defaultLeading:Number = super.getEffectiveFontSize(); |
| return Math.max(defaultLeading, elementHeightWithMarginsAndPadding()); |
| } |
| |
| /** Returns the calculated lineHeight from this element's properties. @private */ |
| tlf_internal override function getEffectiveLineHeight(blockProgression:String):Number |
| { |
| if (effectiveFloat != Float.NONE) |
| return 0; |
| return super.getEffectiveLineHeight(blockProgression); |
| } |
| |
| /** Returns the typographic ascent of the image (i.e. relative to the line's Roman baseline). @private */ |
| tlf_internal function getTypographicAscent(textLine:TextLine):Number |
| { |
| if (effectiveFloat != Float.NONE) |
| return 0; |
| |
| var effectiveHeight:Number = elementHeightWithMarginsAndPadding(); |
| |
| var dominantBaselineString:String; |
| if(this._computedFormat.dominantBaseline != FormatValue.AUTO) |
| { |
| dominantBaselineString = this._computedFormat.dominantBaseline; |
| } |
| else |
| { |
| dominantBaselineString = this.getParagraph().getEffectiveDominantBaseline(); |
| } |
| |
| var elementFormat:ElementFormat = _blockElement ? _blockElement.elementFormat : computeElementFormat(); |
| var alignmentBaseline:String = (elementFormat.alignmentBaseline == flash.text.engine.TextBaseline.USE_DOMINANT_BASELINE ? dominantBaselineString : elementFormat.alignmentBaseline); |
| |
| var top:Number=0; |
| |
| // Calcluate relative to dominant baseline; remains 0 for ASCENT and IDEOGRAPHIC_TOP |
| if (dominantBaselineString == flash.text.engine.TextBaseline.IDEOGRAPHIC_CENTER) |
| top += effectiveHeight/2; |
| else if (dominantBaselineString == flash.text.engine.TextBaseline.IDEOGRAPHIC_BOTTOM || dominantBaselineString == flash.text.engine.TextBaseline.DESCENT || dominantBaselineString == flash.text.engine.TextBaseline.ROMAN) |
| top += effectiveHeight; |
| |
| // re-jig to be relative to the ROMAN baseline rather than whatever baseline is used for alignment |
| top += textLine.getBaselinePosition(flash.text.engine.TextBaseline.ROMAN) - textLine.getBaselinePosition(alignmentBaseline); |
| |
| // finally, account for baseline shift |
| top += elementFormat.baselineShift; |
| |
| return top; |
| } |
| |
| /** @private |
| * Get the "inline box" for the element as defined by the CSS visual formatting model (http://www.w3.org/TR/CSS2/visuren.html) |
| * For an inline graphic, lineHeight is ignored. The box dimensions are governed by the element height with padding. |
| * Alignment relative to the baseline (using baselineShift, dominantBaseline, alignmentBaseline) is taken into account. |
| * @return Null if the element is not "inline" (i.e., is a float). Otherwise, a rectangle representing the inline box. |
| * Top and Bottom are relative to the line's Roman baseline. Left and Right are ignored. |
| */ |
| override tlf_internal function getCSSInlineBox(blockProgression:String, textLine:TextLine, para:ParagraphElement=null, swfContext:ISWFContext=null):Rectangle |
| { |
| if (effectiveFloat != Float.NONE) |
| return null; |
| |
| var inlineBox:Rectangle = new Rectangle(); |
| inlineBox.top = -(getTypographicAscent(textLine)); |
| inlineBox.height = elementHeightWithMarginsAndPadding(); |
| inlineBox.width = elementWidth; |
| |
| return inlineBox; |
| } |
| |
| /** @private */ |
| override tlf_internal function updateIMEAdornments(tLine:TextLine, blockProgression:String, imeStatus:String):void |
| { |
| // Don't underline for floats |
| if (effectiveFloat == Float.NONE) |
| super.updateIMEAdornments(tLine, blockProgression, imeStatus); |
| } |
| |
| /** @private */ |
| override tlf_internal function updateAdornments(tLine:TextLine, blockProgression:String):int |
| { |
| // Don't underline for floats |
| if (effectiveFloat == Float.NONE) |
| return super.updateAdornments(tLine, blockProgression); |
| return 0; |
| } |
| |
| /** @private */ |
| public override function shallowCopy(startPos:int = 0, endPos:int = -1):FlowElement |
| { |
| if (endPos == -1) |
| endPos = textLength; |
| |
| var retFlow:InlineGraphicElement = super.shallowCopy(startPos, endPos) as InlineGraphicElement; |
| retFlow.source = source; |
| retFlow.width = width; |
| retFlow.height = height; |
| retFlow.float = float; |
| |
| return retFlow; |
| } |
| |
| /** @private */ |
| override protected function get abstract():Boolean |
| { return false; } |
| |
| /** @private */ |
| tlf_internal override function get defaultTypeName():String |
| { return "img"; } |
| |
| /** @private */ |
| tlf_internal override function appendElementsForDelayedUpdate(tf:TextFlow,changeType:String):void |
| { |
| if (changeType == ModelChange.ELEMENT_ADDED) |
| tf.incGraphicObjectCount(); |
| else if (changeType == ModelChange.ELEMENT_REMOVAL) |
| tf.decGraphicObjectCount(); |
| |
| if (status != InlineGraphicElementStatus.READY || changeType == ModelChange.ELEMENT_REMOVAL) |
| tf.appendOneElementForUpdate(this); |
| } |
| |
| /** @private */ |
| tlf_internal override function calculateStrikeThrough(tLine:TextLine, blockProgression:String, metrics:FontMetrics):Number |
| { |
| if (!this.graphic || status != InlineGraphicElementStatus.READY) |
| return super.calculateStrikeThrough(tLine,blockProgression,metrics); |
| |
| var stOffset:Number = 0; |
| var inlineHolder:DisplayObjectContainer = _placeholderGraphic.parent; |
| if (inlineHolder) |
| { |
| if(blockProgression != BlockProgression.RL) |
| stOffset = placeholderGraphic.parent.y + (this.elementHeight/2 + Number(getEffectivePaddingTop())); |
| else |
| { |
| var paddingRight:Number = getEffectivePaddingRight(); |
| var line:TextFlowLine = tLine.userData as TextFlowLine; |
| var elemIdx:int = this.getAbsoluteStart() - line.absoluteStart; |
| if(tLine.getAtomTextRotation(elemIdx) != TextRotation.ROTATE_0) |
| stOffset = _placeholderGraphic.parent.x - (this.elementHeight/2 + paddingRight); |
| else |
| stOffset = _placeholderGraphic.parent.x - (this.elementWidth/2 + paddingRight); |
| } |
| } |
| |
| return blockProgression == BlockProgression.TB ? stOffset : -stOffset; |
| } |
| |
| /** @private */ |
| tlf_internal override function calculateUnderlineOffset(stOffset:Number, blockProgression:String, metrics:FontMetrics, tLine:TextLine):Number |
| { |
| if (!this.graphic || status != InlineGraphicElementStatus.READY) |
| return super.calculateUnderlineOffset(stOffset,blockProgression,metrics,tLine); |
| |
| var para:ParagraphElement = this.getParagraph(); |
| var ulOffset:Number = 0; |
| var inlineHolder:DisplayObjectContainer = _placeholderGraphic.parent; |
| if (inlineHolder) |
| { |
| if(blockProgression == BlockProgression.TB) |
| ulOffset = inlineHolder.y + elementHeightWithMarginsAndPadding(); |
| else |
| { |
| if (para.computedFormat.locale.toLowerCase().indexOf("zh") == 0) |
| { |
| ulOffset = inlineHolder.x - elementHeightWithMarginsAndPadding(); |
| ulOffset -= metrics.underlineOffset + (metrics.underlineThickness/2); |
| return ulOffset; |
| } |
| else |
| ulOffset = inlineHolder.x - getEffectivePaddingLeft(); |
| } |
| } |
| ulOffset += metrics.underlineOffset + (metrics.underlineThickness/2); |
| |
| var justRule:String = para.getEffectiveJustificationRule(); |
| if(justRule == JustificationRule.EAST_ASIAN) |
| ulOffset += 1; |
| |
| return ulOffset; |
| } |
| // **************************************** |
| // Begin debug support code |
| // **************************************** |
| |
| /** @private */ |
| CONFIG::debug public override function toString():String |
| { |
| return super.toString() + " " + source; |
| } |
| |
| /** @private */ |
| CONFIG::debug public override function debugCheckFlowElement(depth:int = 0, extraData:String = ""):int |
| { |
| // debugging function that asserts if the flow element tree is in an invalid state |
| |
| var rslt:int = super.debugCheckFlowElement(depth,extraData+" url:"+source); |
| |
| if (_blockElement) |
| rslt += assert(textLength == (_blockElement as GraphicElement).rawText.length,"image is different than its textElement"); |
| rslt += assert(this != getParagraph().getLastLeaf(),"last Leaf in paragraph cannot be image"); |
| |
| return rslt; |
| } |
| } |
| } |