////////////////////////////////////////////////////////////////////////////////
//
// 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 helpers // mx.core
{
import flash.display.DisplayObject;
import flash.display.Graphics;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.StyleSheet;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFieldType;
import flash.text.TextFormat;
import flash.text.TextLineMetrics;
import flash.text.engine.ElementFormat;
import flash.text.engine.FontDescription;
import flash.text.engine.FontLookup;
import flash.text.engine.FontMetrics;
import flash.text.engine.FontPosture;
import flash.text.engine.FontWeight;
import flash.text.engine.Kerning;
import flash.text.engine.LineJustification;
import flash.text.engine.SpaceJustifier;
import flash.text.engine.TextBlock;
import flash.text.engine.TextElement;
import flash.text.engine.TextLine;
import flash.utils.Dictionary;
import flashx.textLayout.compose.ISWFContext;
import flashx.textLayout.compose.TextLineRecycler;
import flashx.textLayout.formats.LeadingModel;
import flashx.textLayout.formats.LineBreak;
import flashx.textLayout.formats.TextDecoration;
import mx.core.FTETextField;
import mx.core.FlexGlobals;
import mx.core.IFlexModuleFactory;
import mx.core.IFontContextComponent;
import mx.core.mx_internal;
use namespace mx_internal;
/**
* FTETextField is a Sprite which displays text by using the new
* Flash Text Engine to implement the old TextField API.
*
* This is a version that was optimized from the Flex 4.1 original. That is included as FTETextField41
*
* These changes had measurable benefits (biggest benefit on top)
* a) reusing TextLines "in place" don't remove them from the parent and readd them. Seems like this shows an issue in player
* b) tracking number of characters and not calling createTextLine the final time to get a null line back
* c) Remove the per FTETextField event listeners and have two listener attached to global application that then fan out all the validateNow calls to all the invalid FTETextFields. Note: Alex isn't sure this is the right way to do this but didn't have a better answer at this time.
* d) two static TextBlock objects so that each composeParagraph didn't need to create a SpaceJustifier
* e) the HTMLHelper class is split into a separate included class
*
* @see flash.text.TextField
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public class FTETextFieldNew extends Sprite implements IFontContextComponent
{
// Current slot count: 21
// (1 for every type except 2 for Number)
//--------------------------------------------------------------------------
//
// Class initialization
//
//--------------------------------------------------------------------------
/**
* @private
*/
private static function initClass():void
{
staticTextBlockAllButLast = new TextBlock();
staticTextBlockAllButLast.textJustifier = new SpaceJustifier("en",LineJustification.ALL_BUT_LAST);
staticTextBlockUnjustified = new TextBlock();
staticTextBlockAllButLast.textJustifier = new SpaceJustifier("en",LineJustification.UNJUSTIFIED);
FlexGlobals.topLevelApplication.addEventListener(Event.RENDER, staticRenderHandler);
FlexGlobals.topLevelApplication.addEventListener(Event.ENTER_FRAME, staticRenderHandler);
}
initClass();
static private var invalidFields:Dictionary = new Dictionary();
/** @private */
static mx_internal function staticRenderHandler(e:Event):void
{
var count:int = 0;
for (var field:Object in invalidFields)
{
(field as FTETextFieldNew).validateNow();
count++;
}
// all done
if (count)
invalidFields = new Dictionary();
}
//--------------------------------------------------------------------------
//
// Class constants
//
//--------------------------------------------------------------------------
/**
* @private
* TextField has fixed 2-pixel padding.
*/
mx_internal static const PADDING_LEFT:Number = 2;
mx_internal static const PADDING_TOP:Number = 2;
mx_internal static const PADDING_RIGHT:Number = 2;
mx_internal static const PADDING_BOTTOM:Number = 2;
/**
* @private
* This regular expression is used to replace LF with CR
* when the text property is set.
*/
private static const ALL_LINEFEEDS:RegExp = /\n/g;
/**
* @private
* Masks for bits inside the 'flags' var
* which store the state of Boolean TextField properties.
*/
private static const FLAG_BACKGROUND:uint = 1 << 0;
private static const FLAG_BORDER:uint = 1 << 1;
private static const FLAG_CONDENSE_WHITE:uint = 1 << 2;
private static const FLAG_EMBED_FONTS:uint = 1 << 3;
private static const FLAG_MULTILINE:uint = 1 << 4;
private static const FLAG_WORD_WRAP:uint = 1 << 5;
/**
* @private
* Masks for bits inside the 'flags' var
* which control what work validateNow() needs to do.
*/
private static const FLAG_TEXT_SET:uint = 1 << 6;
private static const FLAG_HTML_TEXT_SET:uint = 1 << 7;
private static const FLAG_TEXT_LINES_INVALID:uint = 1 << 8;
private static const FLAG_GRAPHICS_INVALID:uint = 1 << 9;
/**
* @private
* Masks for bits inside the 'flags' var
* tracking misc boolean variables.
*/
mx_internal static const FLAG_EFFECTIVE_CONDENSE_WHITE:uint = 1 << 10;
private static const FLAG_VALIDATE_IN_PROGRESS:uint = 1 << 11;
private static const FLAG_HAS_SCROLL_RECT:uint = 1 << 12;
// TODO (gosmith): Does TextField maintain
// an internal vs. external concept of scrollRect?
/**
* @private
*/
private static const ALL_INVALIDATION_FLAGS:uint =
FLAG_TEXT_LINES_INVALID |
FLAG_GRAPHICS_INVALID;
//--------------------------------------------------------------------------
//
// Class variables
//
//--------------------------------------------------------------------------
/**
* @private
* Used for initializing _defaultTextFormat.
*/
private static var textField:TextField = new TextField();
// We can re-use single instances of a few FTE classes over and over,
// since they just serve as a factory for the TextLines that we care about.
/**
* @private
* Create separate TextBlocks for the two different justification styles. Saves recreating the justifier on every compose.
*/
private static var staticTextBlockAllButLast:TextBlock;
private static var staticTextBlockUnjustified:TextBlock;
/**
* @private
*/
private static var staticTextElement:TextElement = new TextElement();
/**
* @private
* This is the max textLine.x + textLine.textWidth of all the composed
* lines. It is used to determine whether the text must be clipped.
*/
mx_internal var clipWidth:Number;
/**
* @private
* Encapsulate all TLF and HTML related code into a helper that is only created when needed.
*/
private var _htmlHelper:HTMLHelper;
//--------------------------------------------------------------------------
//
// Class methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
private static function rint(x:Number):Number
{
var i:Number = Math.round(x);
if (i - 0.5 == x && i & 1)
--i;
return i;
}
/**
* @private
*/
private static function cloneTextFormat(
textFormat:TextFormat):TextFormat
{
var newTextFormat:TextFormat = new TextFormat(
textFormat.font, textFormat.size, textFormat.color,
textFormat.bold, textFormat.italic, textFormat.underline,
textFormat.url, textFormat.target, textFormat.align,
textFormat.leftMargin, textFormat.rightMargin, textFormat.indent,
textFormat.leading);
newTextFormat.blockIndent = textFormat.blockIndent;
newTextFormat.bullet = textFormat.bullet;
newTextFormat.kerning = textFormat.kerning;
newTextFormat.letterSpacing = textFormat.letterSpacing;
newTextFormat.tabStops = textFormat.tabStops;
return newTextFormat;
}
/**
* @private
*/
private static function applyTextFormat(src:TextFormat, dst:TextFormat):void
{
if (src.align != null)
dst.align = src.align;
if (src.blockIndent != null)
dst.blockIndent = src.blockIndent;
if (src.bold != null)
dst.bold = src.bold;
if (src.bullet != null)
dst.bullet = src.bullet;
if (src.color != null)
dst.color = src.color;
if (src.font != null)
dst.font = src.font;
if (src.indent != null)
dst.indent = src.indent;
if (src.italic != null)
dst.italic = src.italic;
if (src.kerning != null)
dst.kerning != src.kerning;
if (src.leading != null)
dst.leading = src.leading;
if (src.leftMargin != null)
dst.leftMargin = src.leftMargin;
if (src.letterSpacing != null)
dst.letterSpacing = src.letterSpacing;
if (src.rightMargin != null)
dst.rightMargin = src.rightMargin;
if (src.size != null)
dst.size = src.size;
if (src.tabStops != null)
dst.tabStops = src.tabStops;
if (src.target != null)
dst.target = src.target;
if (src.underline != null)
dst.underline = src.underline;
if (src.url != null)
dst.url = src.url;
}
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function FTETextFieldNew()
{
super();
// The mouse should not be aware of the TextLines.
// Otherwise, FTETextField will dispatch mouseOver and mouseOut
// events over each line, thich TextField doesn't do.
mouseChildren = false;
doubleClickEnabled = false;
tabChildren = false;
// Use a static TextField to initialize the defaultTextFormat.
// This should be faster than creating a TextFormat object
// and filling it out.
// It will also take care of setting the 'font' field,
// which is platform-dependent.
_defaultTextFormat = textField.defaultTextFormat;
addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
* Apps are likely to create thousands of instances of FTETextField,
* so in order to minimize memory usage we store flags as 1 bit
* inside a uint instead of making each one a 4-byte Boolean var.
*/
private var flags:uint = 0;
/**
* @private
* When we render the text using FTE,
* this object represents the formatting for FTE.
* Every time the defaultTextFormat is set,
* this object is released because it is invalid.
* It is regenerated just in time to render the text.
*/
private var elementFormat:ElementFormat;
//--------------------------------------------------------------------------
//
// Overridden properties: DisplayObject
//
//--------------------------------------------------------------------------
//----------------------------------
// height
//----------------------------------
/**
* @private
*/
private var _height:Number = 100;
/**
* @private
*/
override public function get height():Number
{
// If we're autosizing, _height may be invalid.
// For example, the 'text' may have been set
// but the TextLines for that text haven't
// been created yet.
if (autoSize != TextFieldAutoSize.NONE)
validateNow();
return _height;
}
/**
* @private
*/
override public function set height(value:Number):void
{
// TextField ignores NaN and negative values.
if (isNaN(value) || value < 0)
return;
if (value == _height)
return;
_height = value;
// The TextLines may need to be recreated
// and the border and background may need to be redrawn.
setFlag(FLAG_TEXT_LINES_INVALID |
FLAG_GRAPHICS_INVALID);
invalidate();
}
//----------------------------------
// scrollRect
//----------------------------------
/*
* Workaround for a Flash Player problem.
* Don't read the scrollRect
property if its value has not been set,
* because this will cause a large memory allocation.
* And ignore attempts to reset the scrollRect property to null
* (its default value) if we've never set it.
*/
/**
* @private
*/
override public function get scrollRect():Rectangle
{
return testFlag(FLAG_HAS_SCROLL_RECT) ? super.scrollRect : null;
}
/**
* @private
*/
override public function set scrollRect(value:Rectangle):void
{
if (!testFlag(FLAG_HAS_SCROLL_RECT) && !value)
return;
setFlag(FLAG_HAS_SCROLL_RECT);
super.scrollRect = value;
}
//----------------------------------
// width
//----------------------------------
/**
* @private
*/
private var _width:Number = 100;
/**
* @private
*/
override public function get width():Number
{
// If we're autosizing, _width may be invalid.
// For example, the 'text' may have been set
// but the TextLines for that text haven't
// been created yet.
if (autoSize != TextFieldAutoSize.NONE)
validateNow();
return _width;
}
/**
* @private
*/
override public function set width(value:Number):void
{
// TextField ignores NaN and negative values.
if (isNaN(value) || value < 0)
return;
if (value == _width)
return;
_width = value;
// The TextLines may need to be recreated
// and the border and background may need to be redrawn.
setFlag(FLAG_TEXT_LINES_INVALID |
FLAG_GRAPHICS_INVALID);
invalidate();
}
//--------------------------------------------------------------------------
//
// Properties: TextField
//
//--------------------------------------------------------------------------
//----------------------------------
// alwaysShowSelection
//----------------------------------
/**
* This property is not implemented in FTETextField
* because FTETextField does not support selection.
* Accessing it will throw a runtime error.
*
* @see flash.text.TextField#alwaysShowSelection
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get alwaysShowSelection():Boolean
{
throw new Error(notImplemented("alwaysShowSelection"));
}
/**
* @private
*/
public function set alwaysShowSelection(value:Boolean):void
{
throw new Error(notImplemented("alwaysShowSelection"));
}
//----------------------------------
// antiAliasType
//----------------------------------
/**
* This property has no effect in FTETextField
* because FTE uses a newer font renderer than TextField.
* Getting it will always return null
* and setting it will do nothing.
*
* @see flash.text.TextField#antiAliasType
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get antiAliasType():String
{
return null;
}
/**
* @private
*/
public function set antiAliasType(value:String):void
{
}
//----------------------------------
// autoSize
//----------------------------------
/**
* @private
*/
private var _autoSize:String = TextFieldAutoSize.NONE;
/**
* @copy flash.text.TextField#autoSize
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get autoSize():String
{
return _autoSize;
}
/**
* @private
*/
public function set autoSize(value:String):void
{
// TextField throws this RTE when invalid values are set.
if (value != TextFieldAutoSize.NONE &&
value != TextFieldAutoSize.LEFT &&
value != TextFieldAutoSize.CENTER &&
value != TextFieldAutoSize.RIGHT)
{
var message:String = getErrorMessage("badParameter", "autoSize");
throw new ArgumentError(message);
}
if (value == autoSize)
return;
_autoSize = value;
// The TextLines may need to be recreated
// and the border and background may need to be redrawn.
setFlag(FLAG_TEXT_LINES_INVALID |
FLAG_GRAPHICS_INVALID);
invalidate();
}
//----------------------------------
// background
//----------------------------------
/**
* @copy flash.text.TextField#background
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get background():Boolean
{
return testFlag(FLAG_BACKGROUND);
}
/**
* @private
*/
public function set background(value:Boolean):void
{
if (value == background)
return;
setFlagToValue(FLAG_BACKGROUND, value);
// The border and background need to be redrawn.
setFlag(FLAG_GRAPHICS_INVALID);
invalidate();
}
//----------------------------------
// backgroundColor
//----------------------------------
/**
* @private
*/
private var _backgroundColor:uint = 0xFFFFFF;
/**
* @copy flash.text.TextField#backgroundColor
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get backgroundColor():uint
{
return _backgroundColor;
}
/**
* @private
*/
public function set backgroundColor(value:uint):void
{
if (value == _backgroundColor)
return;
_backgroundColor = value;
// The border and background need to be redrawn.
setFlag(FLAG_GRAPHICS_INVALID);
invalidate();
}
//----------------------------------
// border
//----------------------------------
/**
* @copy flash.text.TextField#border
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get border():Boolean
{
return testFlag(FLAG_BORDER);
}
/**
* @private
*/
public function set border(value:Boolean):void
{
if (value == border)
return;
setFlagToValue(FLAG_BORDER,value);
// The border and background need to be redrawn.
setFlag(FLAG_GRAPHICS_INVALID);
// The border increases the width and height by 1 pixel, so if there
// is a scrollRect, it has to be modified as well.
if (testFlag(FLAG_TEXT_SET | FLAG_HTML_TEXT_SET))
setFlag(FLAG_TEXT_LINES_INVALID);
invalidate();
}
//----------------------------------
// borderColor
//----------------------------------
/**
* @private
*/
private var _borderColor:uint = 0x000000;
/**
* @copy flash.text.TextField#borderColor
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get borderColor():uint
{
return _borderColor;
}
/**
* @private
*/
public function set borderColor(value:uint):void
{
if (value == _borderColor)
return;
_borderColor = value;
// The border and background need to be redrawn.
setFlag(FLAG_GRAPHICS_INVALID);
invalidate();
}
//----------------------------------
// bottomScrollV
//----------------------------------
/**
* This property has not been implemented in FTETextField
* because FTETextField does not support scrolling.
* Accessing it will throw a runtime error.
*
* @see flash.text.TextField#bottomScrollV
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get bottomScrollV():int
{
throw new Error(notImplemented("bottomScrollV"));
}
//----------------------------------
// caretIndex
//----------------------------------
/**
* This property has not been implemented in FTETextField
* because FTETextField does not support editing.
* Accessing it will throw a runtime error.
*
* @see flash.text.TextField#caretIndex
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get caretIndex():int
{
throw new Error(notImplemented("caretIndex"));
}
//----------------------------------
// condenseWhite
//----------------------------------
/**
* @copy flash.text.TextField#condenseWhite
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get condenseWhite():Boolean
{
return testFlag(FLAG_CONDENSE_WHITE);
}
/**
* @private
*/
public function set condenseWhite(value:Boolean):void
{
setFlagToValue(FLAG_CONDENSE_WHITE, value);
// Note: There is nothing else to do immediately;
// the new value doesn't have any effect
// until 'htmlText' is set later.
}
//----------------------------------
// defaultTextFormat
//----------------------------------
/**
* @private
* Storage for the defaultTextFormat property.
* This variable is initialized in the constructor
* to a TextFormat instance filled with default values.
* The setter applies non-null incoming formats
* to the object stored here.
* The getter returns a copy of the object stored here.
* Note that No field of this TextFormat will ever be null.
*/
mx_internal var _defaultTextFormat:TextFormat;
/**
* @copy flash.text.TextField#defaultTextFormat
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get defaultTextFormat():TextFormat
{
// TextField returns a new TextFormat instance each time
// you access defaultTextFormat; the proof is that
// textField.defaultTextFormat != textField.defaultTextFormat
// is true.
return cloneTextFormat(_defaultTextFormat);
}
/**
* @private
*/
public function set defaultTextFormat(value:TextFormat):void
{
// TextField throws this RTE if a null value is set.
if (!value)
{
var message:String = getErrorMessage("nullParameter", "format");
throw new TypeError(message);
}
// Apply non-null formats in the incoming TextFormat
// to the defaultTextFormat.
applyTextFormat(value, _defaultTextFormat);
// These FTE and TLF formatting objects are now invalid
// and must be recreated when needed.
elementFormat = null;
if (_htmlHelper)
_htmlHelper.hostFormat = null;
// Note: Setting this does NOT cause already-rendered text
// to change its format.
// If establishes the formatting for text set or added later.
}
//----------------------------------
// displayAsPassword
//----------------------------------
/**
* This property has not been implemented in FTETextField
* because FTETextField does not support editing.
* Accessing it will throw a runtime error.
*
* @see flash.text.TextField#displayAsPassword
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get displayAsPassword():Boolean
{
throw new Error(notImplemented("displayAsPassword"));
}
/**
* @private
*/
public function set displayAsPassword(value:Boolean):void
{
throw new Error(notImplemented("displayAsPassword"));
}
//----------------------------------
// embedFonts
//----------------------------------
/**
* @copy flash.text.TextField#embedFonts
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get embedFonts():Boolean
{
return testFlag(FLAG_EMBED_FONTS);
}
/**
* @private
*/
public function set embedFonts(value:Boolean):void
{
if (value == embedFonts)
return;
setFlagToValue(FLAG_EMBED_FONTS, value);
// These FTE and TLF formatting objects are now invalid
// and must be recreated when needed.
elementFormat = null;
if (_htmlHelper)
_htmlHelper.hostFormat = null;
// The TextLines may need to be recreated
// and the border and background may need to be redrawn.
setFlag(FLAG_TEXT_LINES_INVALID |
FLAG_GRAPHICS_INVALID);
invalidate();
}
//----------------------------------
// gridFitType
//----------------------------------
/**
* This property has no effect in FTETextField
* because FTE uses a newer font renderer than TextField.
* Getting it will always return null
* and setting it will do nothing.
*
* @see flash.text.TextField#gridFitType
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get gridFitType():String
{
return null;
}
/**
* @private
*/
public function set gridFitType(value:String):void
{
}
//----------------------------------
// htmlText
//----------------------------------
/**
* @copy flash.text.TextField#htmlText
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get htmlText():String
{
// When you set the htmlText and then get it,
// what you get is not necessarily what you set.
// The easiest way to handle this is to make sure
// that the text is composed (which will null out _htmlText
// if there is no styleSheet) and then execute the code
// below to export HTML from the TextFlow.
validateNow();
// When 'text' is set, _htmlText is nulled out
// to indicate that it is invalid
// and must be recalculated.
if (!_htmlHelper)
_htmlHelper = new HTMLHelper(this);
return _htmlHelper.getHtmlText(_text);
}
/**
* @private
*/
public function set htmlText(value:String):void
{
// TextField throws this RTE if a null value is set.
// It seems like this should say
// "Parameter htmlText must be non-null",
// but that's not what TextField does.
if (value == null)
{
var message:String = getErrorMessage("nullParameter", "text");
throw new TypeError(message);
}
// Note: We don't return early if value == _htmlText
// because the defaultTextFormat may have changed
// in which case we need to recompose.
// Remember the value of condenseWhite at the time
// that htmlText is set, because it could be changed
// before the TextLines are rendered.
setFlagToValue(FLAG_EFFECTIVE_CONDENSE_WHITE,
testFlag(FLAG_CONDENSE_WHITE));
if (!_htmlHelper)
_htmlHelper = new HTMLHelper(this);
_htmlHelper._htmlText = value;
_htmlHelper.explicitHTMLText = value;
// _text is now invalid and will get regenerated on demand.
_text = null;
clearFlag(FLAG_TEXT_SET);
setFlag(FLAG_HTML_TEXT_SET |
FLAG_TEXT_LINES_INVALID |
FLAG_GRAPHICS_INVALID);
invalidate();
// NOTE: With hmtlText, what you set is NOT what you get.
// You can set incomplete (or no) markup
// and get back complete markup.
}
//----------------------------------
// length
//----------------------------------
/**
* @copy flash.text.TextField#length
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get length():int
{
return text.length;
}
//----------------------------------
// maxChars
//----------------------------------
/**
* This property has not been implemented in FTETextField
* because FTETextField does not support editing.
* Accessing it will throw a runtime error.
*
* @see flash.text.TextField#maxChars
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get maxChars():int
{
throw new Error(notImplemented("maxChars"));
}
/**
* @private
*/
public function set maxChars(value:int):void
{
throw new Error(notImplemented("maxChars"));
}
//----------------------------------
// maxScrollH
//----------------------------------
/**
* This property has not been implemented in FTETextField
* because FTETextField does not support scrolling.
* Accessing it will throw a runtime error.
*
* @see flash.text.TextField#maxScrollH
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get maxScrollH():int
{
throw new Error(notImplemented("maxScrollH"));
}
//----------------------------------
// maxScrollV
//----------------------------------
/**
* This property has not been implemented in FTETextField
* because FTETextField does not support scrolling.
* Accessing it will throw a runtime error.
*
* @see flash.text.TextField#maxScrollV
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get maxScrollV():int
{
throw new Error(notImplemented("maxScrollV"));
}
//----------------------------------
// mouseWheelEnabled
//----------------------------------
/**
* This property has not been implemented in FTETextField
* because FTETextField does not support scrolling.
* Getting it will always return false
* and setting it will do nothing.
*
* @see flash.text.TextField#mouseWheelEnabled
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get mouseWheelEnabled():Boolean
{
return false;
}
/**
* @private
*/
public function set mouseWheelEnabled(value:Boolean):void
{
}
//----------------------------------
// multiline
//----------------------------------
/**
* This property has no effect in FTETextField
* because FTETextField does not support editing.
* However, you can get and set it.
*
* @see flash.text.TextField#multiline
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get multiline():Boolean
{
return testFlag(FLAG_MULTILINE);
}
/**
* @private
*/
public function set multiline(value:Boolean):void
{
setFlagToValue(FLAG_MULTILINE, value);
}
//----------------------------------
// numLines
//----------------------------------
/**
* @copy flash.text.TextField#numLines
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get numLines():int
{
validateNow();
// All the of the children of this Sprite are TextLines,
// so the number of lines is the number of children.
// TextContainerManager can create Shapes as well,
// but only when using TLF's backgroundColor and backgroundAlpha
// formatting on spans, which FTETextField doesn't use.
return numChildren;
}
//----------------------------------
// restrict
//----------------------------------
/**
* This property has not been implemented in FTETextField
* because FTETextField does not support scrolling.
* Accessing it will throw a runtime error.
*
* @see flash.text.TextField#restrict
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get restrict():String
{
throw new Error(notImplemented("restrict"));
}
/**
* @private
*/
public function set restrict(value:String):void
{
throw new Error(notImplemented("restrict"));
}
//----------------------------------
// scrollH
//----------------------------------
/**
* This property has not been implemented in FTETextField
* because FTETextField does not support scrolling.
* Accessing it will throw a runtime error.
*
* @see flash.text.TextField#scrollH
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get scrollH():int
{
throw new Error(notImplemented("scrollH"));
}
/**
* @private
*/
public function set scrollH(value:int):void
{
throw new Error(notImplemented("scrollH"));
}
//----------------------------------
// scrollV
//----------------------------------
/**
* This property has not been implemented in FTETextField
* because FTETextField does not support scrolling.
* Accessing it will throw a runtime error.
*
* @see flash.text.TextField#scrollV
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get scrollV():int
{
throw new Error(notImplemented("scrollV"));
}
/**
* @private
*/
public function set scrollV(value:int):void
{
throw new Error(notImplemented("scrollV"));
}
//----------------------------------
// selectable
//----------------------------------
/**
* Setting this property has no effect in FTETextField
* because FTETextField does not support selection.
* If you get it, it will always be false
.
*
* @see flash.text.TextField#selectable
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get selectable():Boolean
{
return false;
}
/**
* @private
*/
public function set selectable(value:Boolean):void
{
}
//----------------------------------
// selectionBeginIndex
//----------------------------------
/**
* This property has not been implemented in FTETextField
* because FTETextField does not support selection.
* Accessing it will throw a runtime error.
*
* @see flash.text.TextField#selectionBeginIndex
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get selectionBeginIndex():int
{
throw new Error(notImplemented("selectionBeginIndex"));
}
//----------------------------------
// selectionEndIndex
//----------------------------------
/**
* This property has not been implemented in FTETextField
* because FTETextField does not support selection.
* Accessing it will throw a runtime error.
*
* @see flash.text.TextField#selectionEndIndex
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get selectionEndIndex():int
{
throw new Error(notImplemented("selectionEndIndex"));
}
//----------------------------------
// sharpness
//----------------------------------
/**
* This property has no effect in FTETextField.
* because FTE uses a newer font renderer than TextField.
* Getting it will always return NaN
* and setting it will do nothing.
*
* @see flash.text.TextField#sharpness
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get sharpness():Number
{
return NaN;
}
/**
* @private
*/
public function set sharpness(value:Number):void
{
}
//----------------------------------
// styleSheet
//----------------------------------
/**
* @copy flash.text.TextField#styleSheet
*
* @see flash.text.StyleSheet
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get styleSheet():StyleSheet
{
// Note: TextField does NOT return a copy of the StyleSheet.
return _htmlHelper ? _htmlHelper._styleSheet : null;
}
/**
* @private
*/
public function set styleSheet(value:StyleSheet):void
{
// TextField allows a null value to be set;
// in fact, this is the default.
// Note: We don't return early if value == _styleSheet
// because the same StyleSheet instance could be coming
// in again but might have new values in it.
if (!_htmlHelper)
_htmlHelper = new HTMLHelper(this);
_htmlHelper.styleSheet = value;
// The TextLines may need to be recreated
// and the border and background may need to be redrawn.
setFlag(FLAG_TEXT_LINES_INVALID |
FLAG_GRAPHICS_INVALID);
invalidate();
}
//----------------------------------
// text
//----------------------------------
/**
* @private
*/
private var _text:String = "";
/**
* @copy flash.text.TextField#text
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get text():String
{
// When 'htmlText' is set, _text is nulled out
// to indicate that it is invalid
// and must be rexported from the TextFlow.
if (_text == null)
{
// Convert the LF characters that TLF exports
// into CR characters.
_text = _htmlHelper.getText().replace(ALL_LINEFEEDS, "\r");
}
return _text;
}
/**
* @private
*/
public function set text(value:String):void
{
// TextField throws this RTE if a null value is set.
if (value == null)
{
var message:String = getErrorMessage("nullParameter", "text");
throw new TypeError(message);
}
// Note: We don't return early if value == _text
// because the defaultTextFormat may have changed
// in which case we need to recompose.
// TextField turns all LF characters into CR characters,
// including treating the Windows line-ending-sequence
// CR+LF as two CRs.
_text = value.replace(ALL_LINEFEEDS, "\r");
// _htmlText is now invalid and will get regenerated on demand
_htmlHelper = null;
clearFlag(FLAG_HTML_TEXT_SET);
setFlag(FLAG_TEXT_SET |
FLAG_TEXT_LINES_INVALID |
FLAG_GRAPHICS_INVALID);
invalidate();
}
//----------------------------------
// textColor
//----------------------------------
/**
* @copy flash.text.TextField#textColor
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get textColor():uint
{
// textColor is not an independent format in TextField;
// getting textColor simply returns the color
// in the defaultTextFormat.
return uint(_defaultTextFormat.color);
}
/**
* @private
* Setting the textColor changes the color in the defaultTextFormat
* and redraws the text in the new color.
*/
public function set textColor(value:uint):void
{
if (value == textColor)
return;
_defaultTextFormat.color = value;
// These FTE and TLF formatting objects are now invalid
// and must be recreated when needed.
elementFormat = null;
if (_htmlHelper)
_htmlHelper.hostFormat = null;
setFlag(FLAG_TEXT_LINES_INVALID);
invalidate();
}
//----------------------------------
// textHeight
//----------------------------------
/**
* @private
*/
mx_internal var _textHeight:Number = 0;
/**
* @copy flash.text.TextField#textHeight
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get textHeight():Number
{
validateNow();
return _textHeight;
}
//----------------------------------
// textWidth
//----------------------------------
/**
* @private
*/
mx_internal var _textWidth:Number = 0;
/**
* @copy flash.text.TextField#textWidth
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get textWidth():Number
{
validateNow();
return _textWidth;
}
//----------------------------------
// thickness
//----------------------------------
/**
* This property has no effect in FTETextField
* because FTE uses a newer font renderer than TextField.
* Getting it will always return NaN
* and setting it will do nothing.
*
* @see flash.text.TextField#thickness
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get thickness():Number
{
return NaN;
}
/**
* @private
*/
public function set thickness(value:Number):void
{
}
//----------------------------------
// type
//----------------------------------
/**
* @copy flash.text.TextField#type
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get type():String
{
return TextFieldType.DYNAMIC;
}
/**
* @private
*/
public function set type(value:String):void
{
var message:String;
// TextField throws this RTE when invalid values are set.
if (value != TextFieldType.DYNAMIC &&
value != TextFieldType.INPUT)
{
message = getErrorMessage("badParameter", "type");
throw new ArgumentError(message);
}
if (value == TextFieldType.INPUT)
{
message = getErrorMessage("unsupportedTypeInFTETextField");
throw new Error(message);
}
}
//----------------------------------
// useRichTextClipboard
//----------------------------------
/**
* This property is not implemented in FTETextField
* because FTETextField does not support selection
* or clipboard operations.
* Accessing it will throw a runtime error.
*
* @see flash.text.TextField#useRichTextClipboard
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get useRichTextClipboard():Boolean
{
throw new Error(notImplemented("useRichTextClipboard"));
}
/**
* @private
*/
public function set useRichTextClipboard(value:Boolean):void
{
throw new Error(notImplemented("useRichTextClipboard"));
}
//----------------------------------
// wordWrap
//----------------------------------
/**
* @copy flash.text.TextField#wordWrap
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get wordWrap():Boolean
{
return testFlag(FLAG_WORD_WRAP);
}
/**
* @private
*/
public function set wordWrap(value:Boolean):void
{
if (value == wordWrap)
return;
setFlagToValue(FLAG_WORD_WRAP, value);
// These FTE and TLF formatting objects are now invalid
// and must be recreated when needed.
elementFormat = null;
if (_htmlHelper)
_htmlHelper.hostFormat = null;
// The TextLines may need to be recreated
// and the border and background may need to be redrawn.
setFlag(FLAG_TEXT_LINES_INVALID |
FLAG_GRAPHICS_INVALID);
invalidate();
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// direction
//----------------------------------
/**
* @private
* Storage for the direction property.
*/
private var _direction:String = "ltr";
/**
* The directionality of the text displayed by the FTETextField.
*
*
The allowed values are "ltr"
for left-to-right text,
* as in Latin-style scripts,
* and "rtl"
for right-to-left text,
* as in Arabic and Hebrew.
Note: This property does not exist in the * flash.text.TextField API.
* * @default "ltr" * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function get direction():String { return _direction; } /** * @private */ public function set direction(value:String):void { if (value != "ltr" && value != "rtl") { var message:String = getErrorMessage("badParameter", "direction"); throw new ArgumentError(message); } if (value == _direction) return; _direction = value; // These FTE and TLF formatting objects are now invalid // and must be recreated when needed. elementFormat = null; if (_htmlHelper) _htmlHelper.hostFormat = null; // The TextLines may need to be recreated // and the border and background may need to be redrawn. setFlag(FLAG_TEXT_LINES_INVALID | FLAG_GRAPHICS_INVALID); invalidate(); } //---------------------------------- // fontContext //---------------------------------- /** * @private * Storage for the fontContext property. */ private var _fontContext:IFlexModuleFactory; /** * The IFlexModuleFactory instance that FTETextField * uses for creating TextLine objects. This is usually, but not always, * an ISWFContext. * *Set this if you need lines to be created in a different * SWF context than the one containing the TLF code.
* *Note: This property does not exist in the * flash.text.TextField API.
* * @default null * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function get fontContext():IFlexModuleFactory { return _fontContext; } /** * @private */ public function set fontContext(value:IFlexModuleFactory):void { // FTETextField allows a null value to be set; // in fact, this is the default. if (value == _fontContext) return; _fontContext = value; // The TextLines may need to be recreated // and the border and background may need to be redrawn. setFlag(FLAG_TEXT_LINES_INVALID | FLAG_GRAPHICS_INVALID); invalidate(); } //---------------------------------- // locale //---------------------------------- /** * @private * Storage for the locale property. */ private var _locale:String = "en"; /** * The locale of the text displayed by FTETextField. * *FTE and TLF use this locale to map Unicode characters * to font glyphs and to find fallback fonts.
*/ public function get locale():String { return _locale; } /** * @private */ public function set locale(value:String):void { if (value == _locale) return; _locale = value; // These FTE and TLF formatting objects are now invalid // and must be recreated when needed. elementFormat = null; if (_htmlHelper) _htmlHelper.hostFormat = null; // The TextLines may need to be recreated // and the border and background may need to be redrawn. setFlag(FLAG_TEXT_LINES_INVALID | FLAG_GRAPHICS_INVALID); invalidate(); } //-------------------------------------------------------------------------- // // Properties: Private helpers // //-------------------------------------------------------------------------- /** * @private * The start margin. Used when using FTE to construct the individual * text lines and textLine alignment is done in this class. */ private function get leftMargin():Number { return (direction == "ltr" ? Number(_defaultTextFormat.leftMargin) : Number(_defaultTextFormat.rightMargin)); } /** * @private * The end margin. Used when using FTE to construct the individual * text lines and textLine alignment is done in this class. */ private function get rightMargin():Number { return (direction == "ltr" ? Number(_defaultTextFormat.rightMargin) : Number(_defaultTextFormat.leftMargin)); } //-------------------------------------------------------------------------- // // Methods: TextField // //-------------------------------------------------------------------------- /** * This method has not been implemented in FTETextField * because very few components use it in TextField. * It will throw a runtime error if called. * * @param newText n/a * * @see flash.text.TextField#appendText() * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function appendText(newText:String):void { throw new Error(notImplemented("appendText()")); } /** * This method has not been implemented in FTETextField * because very few components use it in TextField. * It will throw a runtime error if called. * * @param charIndex n/a * * @return n/a * * @see flash.text.TextField#getCharBoundaries() * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function getCharBoundaries(charIndex:int):Rectangle { throw new Error(notImplemented("getCharBoundaries()")); } /** * This method has not been implemented in FTETextField * because very few components use it in TextField. * It will throw a runtime error if called. * * @param x n/a * @param y n/a * * @return n/a * * @see flash.text.TextField#getCharIndexAtPoint() * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function getCharIndexAtPoint(x:Number, y:Number):int { throw new Error(notImplemented("getCharIndexAtPoint()")); } /** * This method has not been implemented in FTETextField * because very few components use it in TextField. * It will throw a runtime error if called. * * @param charIndex n/a * * @return n/a * * @see flash.text.TextField#getFirstCharInParagraph() * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function getFirstCharInParagraph(charIndex:int):int { throw new Error(notImplemented("getFirstCharInParagraph()")); } /** * This method has not been implemented in FTETextField * because very few components use it in TextField. * It will throw a runtime error if called. * * @param x n/a * * @param y n/a * * @return n/a * * @see flash.text.TextField#getLineIndexAtPoint() * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function getLineIndexAtPoint(x:Number, y:Number):int { throw new Error(notImplemented("getLineIndexAtPoint()")); } /** * This method has not been implemented in FTETextField * because very few components use it in TextField. * It will throw a runtime error if called. * * @param charIndex n/a * * @return n/a * * @see flash.text.TextField#getLineIndexOfChar() * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function getLineIndexOfChar(charIndex:int):int { throw new Error(notImplemented("getLineIndexOfChar()")); } /** * This method has not been implemented in FTETextField * because very few components use it in TextField. * It will throw a runtime error if called. * * @param lineIndex n/a * * @return n/a * * @see flash.text.TextField#getLineLength() * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function getLineLength(lineIndex:int):int { throw new Error(notImplemented("getLineLength()")); } /** * @copy flash.text.TextField#getLineMetrics() * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function getLineMetrics(lineIndex:int):TextLineMetrics { validateNow(); // TextField throws this RTE when invalid values are set. if (lineIndex < 0 || lineIndex >= numChildren) { var message:String = getErrorMessage("badIndex"); throw new RangeError(message); } // The nth line is the nth child. var textLine:TextLine = TextLine(getChildAt(lineIndex)); // Convert textLine.x to the global coordinate space. The new point // x is relative to textLine.x. var x:Number = Math.round(textLine.localToGlobal(new Point(0, 0)).x); var width:Number = Math.round(textLine.textWidth); // TextField computes ascent and descent differently than FTE does. // Adding FTE's ascent and descent produces // a reasonable approximation of TextField's ascent. // TextField's ascent, descent, and leading are always rounded. // Rounding FTE's ascent and descent separately, then adding, // produces a "TextField ascent" of 12 + 3 or 15 for Arial 12 // (Flex's default font) on Windows, exactly matching a real // TextField's ascent in this most-common case. var ascent:Number = Math.round(textLine.ascent) + Math.round(textLine.descent) var descent:Number = Math.round(textLine.descent); var leading:Number = Math.round(Number(_defaultTextFormat.leading)); var height:Number = ascent + descent + leading; return new TextLineMetrics(x, width, height, ascent, descent, leading); } /** * This method has not been implemented in FTETextField * because very few components use it in TextField. * It will throw a runtime error if called. * * @param lineIndex n/a * * @return n/a * * @see flash.text.TextField#getLineOffset() * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function getLineOffset(lineIndex:int):int { throw new Error(notImplemented("getLineOffset()")); } /** * This method has not been implemented in FTETextField * because very few components use it in TextField. * It will throw a runtime error if called. * * @param lineIndex n/a * * @return n/a * * @see flash.text.TextField#getLineText() * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function getLineText(lineIndex:int):String { throw new Error(notImplemented("getLineText()")); } /** * This method has not been implemented in FTETextField * because very few components use it in TextField. * It will throw a runtime error if called. * * @param charIndex n/a * * @return n/a * * @see flash.text.TextField#getParagraphLength() * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function getParagraphLength(charIndex:int):int { throw new Error(notImplemented("getParagraphLength()")); } /** * This method has been implemented in FTETextField * to simply return a copy of thedefaultTextFormat
,
* because FTETextField does not support formatting a range.
*
* @param beginIndex n/a
*
* @param endIndex n/a
*
* @return n/a
*
* @see flash.text.TextField#getTextFormat()
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function getTextFormat(beginIndex:int = -1,
endIndex:int = -1):TextFormat
{
// TextField returns a new TextFormat instance each time
// you call getTextFormat(); the proof is that
// textField.getTextFormat() != textField.getTextFormat()
// is true.
return cloneTextFormat(_defaultTextFormat);
}
/**
* This method has not been implemented in FTETextField
* because very few components use it in TextField.
* It will throw a runtime error if called.
*
* @param value n/a
*
* @see flash.text.TextField#replaceSelectedText()
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function replaceSelectedText(value:String):void
{
throw new Error(notImplemented("replaceSelectedText()"));
}
/**
* This method has not been implemented in FTETextField
* because very few components use it in TextField.
* It will throw a runtime error if called.
*
* @param beginIndex n/a
*
* @param endIndex n/a
*
* @param newText n/a
*
* @see flash.text.TextField#replaceText()
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function replaceText(beginIndex:int, endIndex:int,
newText:String):void
{
throw new Error(notImplemented("replaceText()"));
}
/**
* This method has not been implemented in FTETextField
* because FTETextField does not support selection.
* It will throw a runtime error if called.
*
* @param beginIndex n/a
*
* @param endIndex n/a
*
* @see flash.text.TextField#setSelection()
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function setSelection(beginIndex:int, endIndex:int):void
{
throw new Error(notImplemented("setSelection()"));
}
/**
* This method has no effect on a FTETextField if beginIndex
* or endIndex
does not equal -1
* because FTETextField does not support formatting a range.
*
* @param format n/a
*
* @param beginIndex n/a
*
* @param endIndex n/a
*
* @see flash.text.TextField#setTextFormat()
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function setTextFormat(format:TextFormat,
beginIndex:int = -1,
endIndex:int = -1):void
{
if (beginIndex == -1 && endIndex == -1)
{
defaultTextFormat = format;
// The format changed. Some of the attributes such as indent
// and blockIndent require the text to be regenerated.
setFlag(FLAG_TEXT_LINES_INVALID | FLAG_GRAPHICS_INVALID);
validateNow();
}
}
/**
* This method has not been implemented in FTETextField
* because very few components use it in TextField.
* It will throw a runtime error if called.
*
* @param id n/a
*
* @return n/a
*
* @see flash.text.TextField#getImageReference()
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function getImageReference(id:String):DisplayObject
{
throw new Error(notImplemented("getImageReference()"));
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
*/
mx_internal function testFlag(mask:uint):Boolean
{
return (flags & mask) != 0;
}
/**
* @private
*/
mx_internal function setFlag(mask:uint):void
{
flags |= mask;
}
/**
* @private
*/
private function clearFlag(mask:uint):void
{
flags &= ~mask;
}
/**
* @private
*/
private function setFlagToValue(mask:uint, value:Boolean):void
{
if (value)
flags |= mask;
else
flags &= ~mask;
}
/**
* @private
* This method will cause a 'render' event later,
* in response to which validateNow() will get called.
*/
private function invalidate():void
{
if (stage)
stage.invalidate();
invalidFields[this] = 0;
}
/**
* @private
* This method is the workhorse of FTETextField.
* It puts it into a state where all properties are consistent
* with each other and where it is rendering what the properties
* specify.
*/
private function validateNow():void
{
// TODO (gosmith): When do we get recursive validateNow()?
if (!testFlag(ALL_INVALIDATION_FLAGS) ||
testFlag(FLAG_VALIDATE_IN_PROGRESS))
{
return;
}
setFlag(FLAG_VALIDATE_IN_PROGRESS);
if (testFlag(FLAG_TEXT_LINES_INVALID))
{
// Remove the previous TextLines
// (and recycle them, if supported by the player).
_textWidth = 0;
_textHeight = 0;
clipWidth = 0;
// Determine the composition width and height.
var compositionWidth:Number = NaN;
var compositionHeight:Number = NaN;
if (_autoSize == TextFieldAutoSize.NONE)
{
compositionWidth = _width;
compositionHeight = _height;
}
else if (wordWrap)
{
compositionWidth = _width;
}
if (testFlag(FLAG_HTML_TEXT_SET))
{
if (!_htmlHelper.hostFormat)
createHostFormat();
_htmlHelper.composeHTMLText(compositionWidth, compositionHeight);
}
else
{
if (!elementFormat)
createElementFormat();
composeText(compositionWidth, compositionHeight);
}
var origX:Number = x;
var origWidth:Number = _width;
var origHeight:Number = _height;
if (_autoSize != TextFieldAutoSize.NONE)
{
_height = _textHeight + PADDING_TOP + PADDING_BOTTOM;
if (!wordWrap)
{
_width = _textWidth + PADDING_LEFT + PADDING_RIGHT;
var blockIndent:Number = Number(_defaultTextFormat.blockIndent);
var indent:Number = Number(_defaultTextFormat.indent);
// Factor in indents and margins if the combined total
// is positive.
if (blockIndent + indent + leftMargin > 0)
_width += blockIndent + indent + leftMargin;
// Right margin seems to always be considered but if its
// negative the width can't get smaller than the text width.
_width += rightMargin;
if (rightMargin > 0)
{
clipWidth = _width;
}
else
{
if (_width - PADDING_LEFT - PADDING_RIGHT < _textWidth )
_width = _textWidth + PADDING_LEFT + PADDING_RIGHT;
// force clipping
clipWidth = origWidth + 1;
}
// adjust x for CENTER and RIGHT cases
if (_autoSize == TextFieldAutoSize.RIGHT)
x += origWidth - _width;
else if (_autoSize == TextFieldAutoSize.CENTER)
x += (origWidth - _width) / 2;
}
if (_height != origHeight || _width != origWidth || x != origX)
setFlag(FLAG_GRAPHICS_INVALID);
}
if (clipWidth > origWidth || _textHeight > origHeight)
{
// need to clip
//trace("clip", "_textWidth", _textWidth, "origWidth", origWidth);
var r:Rectangle = scrollRect;
if (!r)
r = new Rectangle();
r.left = 0;
r.top = 0;
r.right = _width;
r.bottom = _height;
// Expand scrollRect by one pixel so the bottom and right
// borders are not cliped. See note below.
if (testFlag(FLAG_GRAPHICS_INVALID) && border)
{
r.width++;
r.height++;
}
scrollRect = r;
}
else
{
// don't need to clip
//trace("don't clip", "_textWidth", _textWidth, "origWidth", origWidth);
scrollRect = null;
}
}
// Draw the border and background last,
// once the width and height are known.
if (testFlag(FLAG_GRAPHICS_INVALID))
{
var g:Graphics = graphics;
g.clear();
// First draw the background, then draw the border.
// This is because TextField actually does something strange --- it expands itselft 1 pixel right and down when drawing a border
// and fill without the stroke with the required stroking path does not match the "background sans border" behavior of TextField.
// Width/Height rounding differences between TextField and FTETextField...
// For width or height of the form E.5 where E is a positive even integer, Flash 10 on Windows seems to
// "round to even", i.e., round the dimension down to E rather than up to E+1. However we currently just
// round consistently up to E+1 using Math.round() here since for now are willing to live with this difference.
var w:Number = rint(_width);
var h:Number = rint(_height);
// Even if no background is requested, we fill the bounds
// with alpha=0 pixels so that mouse events are generated.
g.beginFill(backgroundColor, background ? 1.0 : 0.0);
g.drawRect(0, 0, w, h);
g.endFill();
if (border)
{
g.lineStyle(1, borderColor);
g.drawRect(0.5, 0.5, _width, _height); // TextField actually expands by a pixel down and to the right when it has a border!
}
}
clearFlag(ALL_INVALIDATION_FLAGS | FLAG_VALIDATE_IN_PROGRESS);
}
/**
* @private
*/
private function createElementFormat():void
{
var fontDescription:FontDescription = new FontDescription();
fontDescription.fontLookup = embedFonts ?
FontLookup.EMBEDDED_CFF :
FontLookup.DEVICE;
fontDescription.fontName = _defaultTextFormat.font;
fontDescription.fontPosture = _defaultTextFormat.italic ?
FontPosture.ITALIC :
FontPosture.NORMAL;
fontDescription.fontWeight = _defaultTextFormat.bold ?
FontWeight.BOLD :
FontWeight.NORMAL;
elementFormat = new ElementFormat();
elementFormat.color = uint(_defaultTextFormat.color);
elementFormat.fontDescription = fontDescription;
elementFormat.fontSize = Number(_defaultTextFormat.size);
elementFormat.kerning = _defaultTextFormat.kerning ?
Kerning.AUTO :
Kerning.OFF;
elementFormat.locale = locale;
elementFormat.trackingRight = Number(_defaultTextFormat.letterSpacing);
}
/**
* @private
*/
private function createHostFormat():void
{
_htmlHelper.hostFormat = new FTETextFieldHostFormat(this);
}
// used during compose to track nextLineIndex
static private var nextLineIndex:int;
/**
* @private
*/
private function removeExcessTextLines():void
{
var n:int = numChildren;
for (var i:int = nextLineIndex; i < n; i++)
{
// Repeatedly removing the 0th child is supposed
// to be the fastest way to remove all children.
var textLine:TextLine = TextLine(removeChildAt(nextLineIndex));
// TLF provides a TextLine cache,
// for use with recreateTextLine().
TextLineRecycler.addLineForReuse(textLine);
}
}
/**
* @private
*/
private function composeText(compositionWidth:Number,
compositionHeight:Number):void
{
var innerWidth:Number =
compositionWidth - PADDING_LEFT - PADDING_RIGHT;
var innerHeight:Number =
compositionHeight - PADDING_TOP - PADDING_BOTTOM;
// FTE's emBox's top gives the ascent and its bottom gives the descent.
// TextField computes ascent and descent differently than FTE does.
// Adding FTE's ascent and descent produces
// a reasonable approximation of TextField's ascent.
// TextField's ascent, descent, and leading are always rounded.
// Rounding FTE's ascent and descent separately, then adding,
// produces a "TextField ascent" of 12 + 3 or 15 for Arial 12
// (Flex's default font) on Windows, exactly matching a real
// TextField's ascent in this most-common case.
var emBox:Rectangle;
if (fontContext)
emBox = fontContext.callInContext(elementFormat.getFontMetrics, elementFormat, []).emBox;
else
emBox = elementFormat.getFontMetrics().emBox;
var ascent:int = Math.round(-emBox.top) + Math.round(emBox.bottom);
var descent:int = Math.round(emBox.bottom);
var leading:Number = Math.round(Number(_defaultTextFormat.leading));
// Break the text into paragraphs at CR characters.
// (Each LF character has already been turned into a CR.)
// We could use split(), but that would create a temporary Array.
var paragraphY:int = 0;
var n:int = text.length;
var i:int = 0;
nextLineIndex = 0;
do
{
var j:int = text.indexOf("\r", i);
if (j == -1)
j = n;
var paragraphText:String = i == 0 && j == n ?
text :
text.substring(i, j);
// Use an FTE TextBlock to compose TextLines
// for one paragraph of the text, keeping track
// of how far down we've composed.
paragraphY = createTextLines(innerWidth, innerHeight,
paragraphText, paragraphY,
ascent, descent);
// TextField puts the same leading between paragraphs
// as between lines in a paragraph.
paragraphY += leading;
i = j + 1;
}
while (j < n);
removeExcessTextLines();
// At this point, all TextLines have been composed
// and have the correct spacing, but are all left-aligned
// starting at (0, 0).
// This method will adjust their x and y so that they
// are correctly aligned and inset by the left and top padding and
// indent and margins.
alignTextLines(innerWidth);
_textWidth = Math.round(_textWidth);
_textHeight = Math.round(
numChildren * (ascent + descent) +
(numChildren - 1) * Number(_defaultTextFormat.leading));
clipWidth = Math.round(clipWidth);
}
/**
* @private
* Stuffs the specified paragraph text and formatting info into a TextBlock
* and uses it to create as many TextLines as fit into the bounds.
* Returns true if all the text was composed into textLines.
*/
private function createTextLines(innerWidth:Number,
innerHeight:Number,
paragraphText:String,
paragraphY:int,
ascent:int, descent:int):int
{
var textBlock:TextBlock = _defaultTextFormat.align == "justify" ? staticTextBlockAllButLast : staticTextBlockUnjustified;
// Set the TextBlock's content.
// Note: If there is no text, we do what TLF does and compose
// a paragraph terminator character, so that a TextLine
// gets created and we can measure it.
// It will have a width of 0 but a height equal
// to the font's ascent plus descent.
staticTextElement.text = paragraphText.length > 0 ?
paragraphText :
"\u2029";
staticTextElement.elementFormat = elementFormat;
textBlock.content = staticTextElement;
// And its bidiLevel.
textBlock.bidiLevel = direction == "ltr" ? 0 : 1;
// Then create and add TextLines using this TextBlock.
paragraphY = createTextLinesFromTextBlock(
innerWidth, innerHeight,
textBlock, staticTextElement.text.length, paragraphY,
ascent, descent);
// Cleans up and sets the validity of the lines associated
// with the TextBlock to TextLineValidity.INVALID.
var firstLine:TextLine = textBlock.firstLine;
if (firstLine)
textBlock.releaseLines(firstLine, textBlock.lastLine);
return paragraphY;
}
/**
* @private
* Compose into textLines. bounds on input is size of composition
* area and on output is the size of the composed content.
* The caller must call releaseLinesFromTextBlock() to release the
* textLines from the TextBlock.
*
* Returns true if all the text was composed into textLines.
*/
private function createTextLinesFromTextBlock(innerWidth:Number,
innerHeight:Number,
textBlock:TextBlock, textLength:int,
paragraphY:int,
ascent:int,
descent:int):int
{
if (innerWidth < 0 || innerHeight < 0)
return paragraphY;
var blockIndent:Number = Number(_defaultTextFormat.blockIndent);
var indent:Number = Number(_defaultTextFormat.indent);
var maxLineWidthBeforeIndent:Number =
wordWrap ? innerWidth : TextLine.MAX_LINE_WIDTH;
var maxLineWidth:Number = maxLineWidthBeforeIndent;
var n:int = 0;
var nextTextLine:TextLine;
var nextY:int = paragraphY;
var textLine:TextLine;
const thisLeftMargin:Number = leftMargin;
const thisRightMargin:Number = rightMargin;
// TextField seems to do this. You can see it with wordWrap and
// indent > width or when rightMargin > width. In the former case,
// the first line is visually empty but contains a character which is
// clipped and the second line starts with the second letter.
// The clipped first line serves as a placeholder so that the rest
// of the lines which may be visible are composed.
var fitSomething:Boolean = true;
var charsConsumed:int = 0;
// Generate TextLines, stopping when we run out of text
// or reach the bottom of the requested bounds.
// In this loop the lines are positioned within the rectangle
// (0, 0, innerWidth, innerHeight), with left alignment.
while (charsConsumed < textLength)
{
// Adjust the compose width for indents and margins.
if (n <= 1)
{
var totalIndent:Number = blockIndent + thisLeftMargin;
if (n == 0)
totalIndent += indent;
if (totalIndent < 0)
totalIndent = 0;
else if (totalIndent > _width - PADDING_LEFT - PADDING_RIGHT)
totalIndent = _width - PADDING_LEFT - PADDING_RIGHT;
maxLineWidth =
maxLineWidthBeforeIndent - totalIndent;
if (wordWrap)
maxLineWidthBeforeIndent -= thisRightMargin;
// Stay within the bounds to avoid exception. Since
// fitSomething is true it is okay if maxLineWidth is < 0.
if (maxLineWidth > TextLine.MAX_LINE_WIDTH)
maxLineWidth = TextLine.MAX_LINE_WIDTH;
}
var recycleLine:TextLine = (nextLineIndex < numChildren) ? (getChildAt(nextLineIndex) as TextLine) : TextLineRecycler.getLineForReuse();
if (recycleLine)
{
if (fontContext as ISWFContext)
{
nextTextLine = fontContext.callInContext(
textBlock["recreateTextLine"], textBlock,
[ recycleLine, textLine, maxLineWidth, 0.0, fitSomething ]);
}
else
{
nextTextLine = textBlock["recreateTextLine"](
recycleLine, textLine, maxLineWidth, 0.0, fitSomething);
}
}
else
{
if (fontContext as ISWFContext)
{
nextTextLine = fontContext.callInContext(
textBlock.createTextLine, textBlock,
[ textLine, maxLineWidth, 0.0, fitSomething ]);
}
else
{
nextTextLine = textBlock.createTextLine(
textLine, maxLineWidth, 0.0, fitSomething);
}
}
nextTextLine.doubleClickEnabled = false;
charsConsumed += nextTextLine.rawTextLength;
// Determine the natural baseline position for this line.
// Note: The y coordinate of a TextLine is the location
// of its baseline, not of its "top".
if (n == 0)
nextY += ascent;
else
nextY += descent + _defaultTextFormat.leading + ascent;
// We'll keep this line.
textLine = nextTextLine;
n++;
// Assign its location based on left/top alignment.
// Its x position is 0 by default.
textLine.y = nextY;
// Adjust for positive indent/left margin. Do it here rather
// than at the end when alignment is done so the first
// line of each paragraph is indented properly.
textLine.x = totalIndent;
if (_defaultTextFormat.underline)
{
// FTE doesn't render underlines,
// but it can tell us where to draw them.
// You can't draw in a TextLine but it can have children,
// so we create a child Shape to draw them in.
var fontMetrics:FontMetrics;
if (fontContext)
fontMetrics = fontContext.callInContext(elementFormat.getFontMetrics, elementFormat, []);
else
fontMetrics = elementFormat.getFontMetrics();
var shape:Shape = new Shape();
var g:Graphics = shape.graphics;
g.lineStyle(fontMetrics.underlineThickness,
elementFormat.color, elementFormat.alpha);
g.moveTo(0, fontMetrics.underlineOffset);
g.lineTo(textLine.textWidth, fontMetrics.underlineOffset);
textLine.addChild(shape);
}
if (nextLineIndex == numChildren)
addChild(textLine);
nextLineIndex++;
}
return nextY + descent;
}
/**
* @private
* Returns with _textWidth and clipWidth set.
*/
private function alignTextLines(innerWidth:Number):void
{
// This is only the case when we are auto sizing. In this case
// we don't want to do any alignment.
if (isNaN(innerWidth))
innerWidth = 0;
var align:String = _defaultTextFormat.align;
var leftAligned:Boolean =
align == "left" ||
align == "justify" && direction == "ltr";
var centerAligned:Boolean = align == "center";
var rightAligned:Boolean =
align == "right" ||
align == "justify" && direction == "rtl";
// Calculate loop constants for horizontal alignment.
var leftOffset:Number = PADDING_LEFT;
var centerOffset:Number = leftOffset + innerWidth / 2;
var rightOffset:Number = leftOffset + innerWidth;
const thisRightMargin:Number = rightMargin;
// Reposition each line if necessary.
// based on the horizontal alignment,
// and adjusting for the padding.
var n:int = numChildren;
for (var i:int = 0; i < n; i++)
{
var textLine:TextLine = TextLine(getChildAt(i));
_textWidth = Math.max(_textWidth, textLine.textWidth);
var width:Number = textLine.x + textLine.textWidth + thisRightMargin;
// Only align if there is width to do so.
if (leftAligned || (width >= innerWidth && direction == "ltr"))
textLine.x += leftOffset;
else if (rightAligned || (width >= innerWidth && direction == "rtl"))
textLine.x += rightOffset - width;
else if (centerAligned)
textLine.x += centerOffset - width / 2;
// If x < 0 then need to force clipping in validateNow() so text
// won't leak in this direction.
if (textLine.x < 0)
clipWidth = int.MAX_VALUE;
else
clipWidth = Math.max(clipWidth, textLine.x + textLine.textWidth);
textLine.y += PADDING_TOP;
}
}
/**
* @private
* Provides RTE messages.
* FTETextField is deliberately kept independent
* of the rest of the Flex framework.
* Therefore it doesn't have access to localized resource strings
* in the ResourceManager and simply has hard-coded English Strings.
* However, framework subclasses such as UIFTETextField override
* this method to provide localized messages from ResourceManager.
*/
mx_internal function getErrorMessage(key:String, param:String = null):String
{
var message:String = "";
switch (key)
{
case "badParameter":
{
// This message matches the one in Flash Player.
message = "Parameter " + param + " must be one of the accepted values.";
break;
}
case "nullParameter":
{
// This message matches the one in Flash Player.
message = "Parameter " + param + " must be non-null.";
break;
}
case "badIndex":
{
// This message matches the one in Flash Player.
message = "The supplied index is out of bounds.";
break;
}
case "notImplementedInFTETextField":
{
message = "'" + param + "' is not implemented in FTETextFieldNew.";
break;
}
case "unsupportedTypeInFTETextField":
{
message = "FTETextFieldNew does not support setting type to \"input\".";
break;
}
}
return message;
}
/**
* @private
*/
private function notImplemented(name:String):String
{
return getErrorMessage("notImplementedInFTETextField", name);
}
//--------------------------------------------------------------------------
//
// Event handlers
//
//--------------------------------------------------------------------------
/**
* @private
*/
private function addedToStageHandler(event:Event):void
{
invalidate();
}
/**
* @private
*/
mx_internal function inlineGraphicStatusChangeHandler(
event:Event):void
{
setFlag(FLAG_TEXT_LINES_INVALID |
FLAG_GRAPHICS_INVALID);
invalidate();
}
}
}
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.TextEvent;
import flash.geom.Rectangle;
import flash.text.StyleSheet;
import flash.text.engine.FontLookup;
import flash.text.engine.FontPosture;
import flash.text.engine.FontWeight;
import flash.text.engine.Kerning;
import flashx.textLayout.compose.ISWFContext;
import flashx.textLayout.container.TextContainerManager;
import flashx.textLayout.conversion.ConversionType;
import flashx.textLayout.conversion.ITextExporter;
import flashx.textLayout.conversion.ITextImporter;
import flashx.textLayout.conversion.TextConverter;
import flashx.textLayout.edit.EditingMode;
import flashx.textLayout.elements.Configuration;
import flashx.textLayout.elements.FlowElement;
import flashx.textLayout.elements.LinkElement;
import flashx.textLayout.elements.IConfiguration;
import flashx.textLayout.elements.IFormatResolver;
import flashx.textLayout.elements.LinkElement;
import flashx.textLayout.elements.ParagraphElement;
import flashx.textLayout.elements.SpanElement;
import flashx.textLayout.elements.TextFlow;
import flashx.textLayout.events.FlowElementMouseEvent;
import flashx.textLayout.events.StatusChangeEvent;
import flashx.textLayout.factory.TextFlowTextLineFactory;
import flashx.textLayout.formats.ITextLayoutFormat;
import flashx.textLayout.formats.LeadingModel;
import flashx.textLayout.formats.LineBreak;
import flashx.textLayout.formats.TextDecoration;
import flashx.textLayout.formats.TextLayoutFormat;
import mx.core.mx_internal;
import helpers.FTETextFieldNew;
use namespace mx_internal;
/**
* @private
*/
class FTETextFieldTextContainerManager extends TextContainerManager
{
/**
* @private
*/
public function FTETextFieldTextContainerManager(container:Sprite, configuration:IConfiguration = null)
{
super(container, configuration);
}
/**
* @private
*/
override public function drawBackgroundAndSetScrollRect(scrollX:Number, scrollY:Number):Boolean
{
return true;
}
}
/**
* @private
*/
class FTETextFieldHostFormat extends TextLayoutFormat
{
public function FTETextFieldHostFormat(textField:FTETextFieldNew)
{
super();
this.textField = textField;
}
private var textField:FTETextFieldNew;
public override function get color():*
{
return textField._defaultTextFormat.color;
}
public override function get direction():*
{
return textField.direction;
}
public override function get fontFamily():*
{
return textField._defaultTextFormat.font;
}
public override function get fontLookup():*
{
return textField.embedFonts ?
FontLookup.EMBEDDED_CFF :
FontLookup.DEVICE;
}
public override function get fontSize():*
{
return textField._defaultTextFormat.size;
}
public override function get fontStyle():*
{
return textField._defaultTextFormat.italic ?
FontPosture.ITALIC :
FontPosture.NORMAL;
}
public override function get fontWeight():*
{
return textField._defaultTextFormat.bold ?
FontWeight.BOLD :
FontWeight.NORMAL;
}
public override function get kerning():*
{
return textField._defaultTextFormat.kerning ?
Kerning.AUTO :
Kerning.OFF;
}
public override function get leadingModel():*
{
return LeadingModel.APPROXIMATE_TEXT_FIELD;
}
public override function get lineBreak():*
{
return textField.wordWrap ?
LineBreak.TO_FIT :
LineBreak.EXPLICIT;
}
public override function get lineHeight():*
{
return textField._defaultTextFormat.leading;
}
public override function get locale():*
{
return textField.locale;
}
public override function get paddingBottom():*
{
return FTETextFieldNew.PADDING_BOTTOM;
}
public override function get paddingLeft():*
{
return FTETextFieldNew.PADDING_LEFT;
}
public override function get paddingRight():*
{
return FTETextFieldNew.PADDING_RIGHT;
}
public override function get paddingTop():*
{
return FTETextFieldNew.PADDING_TOP;
}
public override function get paragraphEndIndent():*
{
return textField._defaultTextFormat.rightMargin;
}
public override function get paragraphStartIndent():*
{
return textField._defaultTextFormat.leftMargin;
}
public override function get textAlign():*
{
return textField._defaultTextFormat.align;
}
public override function get textAlignLast():*
{
return textField._defaultTextFormat.align;
}
public override function get textDecoration():*
{
return textField._defaultTextFormat.underline ?
TextDecoration.UNDERLINE :
TextDecoration.NONE;
}
public override function get textIndent():*
{
return textField._defaultTextFormat.indent;
}
public override function get trackingRight():*
{
return textField._defaultTextFormat.letterSpacing;
}
}
/**
* @private
* To attach TextField styling via a style sheet to a TextFlow.
*/
class FTETextFieldStyleResolver implements IFormatResolver
{
//--------------------------------------------------------------------------
//
// Class variables
//
//--------------------------------------------------------------------------
/**
* Map of TextField StyleSheet CSS properties to their equivalent
* TLF properties. This is only the styles which have different names.
*/
private static const textFieldToTLFStyleMap:Object =
{
"leading": "lineHeight",
"letterSpacing": "trackingRight",
"marginLeft": "paragraphStartIndent",
"marginRight": "paragraphEndIndent"
};
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
public function FTETextFieldStyleResolver(styleSheet:StyleSheet):void
{
_styleSheet = styleSheet;
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* The TextField style sheet that will be used to create the TLF style
* objects.
*/
private var _styleSheet:StyleSheet;
//--------------------------------------------------------------------------
//
// IFormatResolver
//
//--------------------------------------------------------------------------
/**
* Given a FlowElement or ContainerController object, return any format
* settings for it.
*
* Return format settings for the specified object.
*/
public function resolveFormat(elem:Object):ITextLayoutFormat
{
var attr:TextLayoutFormat = null;
// ContainerController will inherit via the container so it is not
// handled here. Map HTML to TextFlow, HTML to // ParagraphElement and HTML to SpanElement. if (elem is FlowElement) { if (elem is TextFlow) attr = addStyleAttributes(attr, "body"); else if (elem is ParagraphElement) attr = addStyleAttributes(attr, "p"); else if (elem is SpanElement) attr = addStyleAttributes(attr, "span"); // Apply class selector over any format from above. if (elem.styleName != null) attr = addStyleAttributes(attr, "." + elem.styleName); } return attr; } /** * Given a FlowElement or ContainerController object and the name of a * format property, return the format value or undefined if * the value is not found. * * Return the value of the specified format for the specified object. */ public function resolveUserFormat(elem:Object,userStyle:String):* { var flowElem:FlowElement = elem as FlowElement; var attr:TextLayoutFormat; // support non-tlf styles if (flowElem) { if (flowElem.styleName) { attr = addStyleAttributes(null, "." + flowElem.styleName); } else if (flowElem is LinkElement) { if (userStyle == "linkNormalFormat") attr = addStyleAttributes(null, "a:link"); else if (userStyle == "linkHoverFormat") attr = addStyleAttributes(null, "a:hover"); else if (userStyle == "linkActiveFormat") attr = addStyleAttributes(null, "a:active"); } else { attr = addStyleAttributes(null, userStyle); } } return attr != null ? attr : undefined; } /** * Invalidates any cached formatting information for a TextFlow so that * formatting must be recomputed. */ public function invalidateAll(tf:TextFlow):void { } /** * Invalidates cached formatting information on this element because, for * example, the parent changed, or the id or the styleName changed. */ public function invalidate(target:Object):void { } /** * Return the format resolver for the copy of the TextFlow. */ public function getResolverForNewFlow(oldFlow:TextFlow,newFlow:TextFlow):IFormatResolver { return this; } //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- /** * Look up the styleSelector in the TextField style sheet and build the * object of corresponding TLF styles and values. Return null if the * styleSelector is not found in the style sheet. */ private function addStyleAttributes( attr:TextLayoutFormat, styleSelector:String):TextLayoutFormat { var foundStyle:Object = _styleSheet.getStyle(styleSelector); if (foundStyle != null) { for (var p:* in foundStyle) { var propStyle:Object = foundStyle[p]; if (attr == null) attr = new TextLayoutFormat(); if (textFieldToTLFStyleMap[p]) { // different name, same values var tlfProp:String = textFieldToTLFStyleMap[p]; attr[tlfProp] = propStyle; } else if (p == "color") { // convert from "#000000" to "0x000000" format var color:String = String(propStyle); if (color && color.charAt(0) == "#") attr.color = "0x"+color.substring(1); } else if (p == "display") { // TODO(cframpto): if we decide to support this. } else if (p == "kerning") { // convert from true/false to on/off if (Boolean(propStyle)) attr.kerning = flash.text.engine.Kerning.ON; else attr.kerning = flash.text.engine.Kerning.OFF; } else { // same name, same values attr[p] = propStyle; } } } return attr; } } /** @private Used when needing an HTML view of things. */ class HTMLHelper { /** * @private */ private static var plainTextImporter:ITextImporter; /** * @private */ private static var plainTextExporter:ITextExporter; /** * @private */ private static var collapsingHTMLImporter:ITextImporter; /** * @private */ private static var preservingHTMLImporter:ITextImporter; /** * @private */ private static var htmlExporter:ITextExporter; /** * @private */ private static function initClass():void { var format:TextLayoutFormat; var config:Configuration; // Create an importer for plain text. plainTextImporter = TextConverter.getImporter(TextConverter.PLAIN_TEXT_FORMAT); // Create an exporter for plain text. plainTextExporter = TextConverter.getExporter(TextConverter.PLAIN_TEXT_FORMAT); // Create an importer for TEXT_FIELD_HTML_FORMAT that collapses whitespace. // Note: We have to make a copy of the textFlowInitialFormat, // which has various formats set to "inherit", // and then modify it and set it back. config = new Configuration(); format = new TextLayoutFormat(config.textFlowInitialFormat); format.whiteSpaceCollapse = "collapse"; config.textFlowInitialFormat = format; collapsingHTMLImporter = TextConverter.getImporter(TextConverter.TEXT_FIELD_HTML_FORMAT, config); collapsingHTMLImporter.throwOnError = false; // Create an importer for TEXT_FIELD_HTML_FORMAT that preserves whitespace. // Note: We have to make a copy of the textFlowInitialFormat, // which has various formats set to "inherit", // and then modify it and set it back. config = new Configuration(); format = new TextLayoutFormat(config.textFlowInitialFormat); format.whiteSpaceCollapse = "preserve"; config.textFlowInitialFormat = format; preservingHTMLImporter = TextConverter.getImporter(TextConverter.TEXT_FIELD_HTML_FORMAT, config); preservingHTMLImporter.throwOnError = false; // Create an exporter for TEXT_FIELD_HTML_FORMAT. htmlExporter = TextConverter.getExporter(TextConverter.TEXT_FIELD_HTML_FORMAT); } initClass(); /** * @private */ private static var factory:TextFlowTextLineFactory = new TextFlowTextLineFactory(); private var _field:FTETextFieldNew; public function HTMLHelper(field:FTETextFieldNew) { this._field = field; } /** * @private * If htmlText is set, composeHtmlText is called each time the text lines * are invalidated. The setters for many of the properties invalidate * the text lines. So although _htmlText is set to null, if there is no * styleSheet, to indicate to the htmlText getter that it needs to import * the html from the textFlow, we need the orginal htmlText for the cases * where we need to regenerate the html text lines. */ public var explicitHTMLText:String = null; /** * @private * When we render the htmlText using TLF, * this object represents the formatting for TLF. * Every time the defaultTextFormat is set, * this object is released because it is invalid. * It is regenerated just in time to render the htmlText. */ public var hostFormat:ITextLayoutFormat; /** * @private * When we render the htmlText using TLF, * this object represents the rich text to be displayed. * It is created by using TLF's HTML importer to import the htmlText. */ public var textFlow:TextFlow; /** * @private * When we render the htmlText using TLF, * this object composes the textFlow * (with the hostFormat applied to it) * to create TextLines in this Sprite. */ public var textContainerManager:TextContainerManager; /** * @private */ public var _htmlText:String = null; /** * @private */ public var _styleSheet:StyleSheet = null; //---------------------------------- // htmlImporter //---------------------------------- /** * @private */ private function get htmlImporter():ITextImporter { // Note that which importer we return depends on the value // of condenseWhite was at the time that htmlText was set, // not on the current value of condenseWhite, // since it could change between htmlText being set // and the TextLines being composed. return _field.testFlag(FTETextFieldNew.FLAG_EFFECTIVE_CONDENSE_WHITE) ? collapsingHTMLImporter : preservingHTMLImporter; } /** * @private */ public function composeHTMLText(compositionWidth:Number, compositionHeight:Number):void { var textFlow:TextFlow = textFlow = htmlImporter.importToFlow(explicitHTMLText); // Unless there is a styleSheet, _htmlText is now invalid // and needs to be regenerated on demand, // because with htmlText what-you-set-is-not-what-you-get. if (!_styleSheet) _htmlText = null; if (!textFlow) return; textFlow.addEventListener(MouseEvent.CLICK, linkClickHandler); textFlow.addEventListener( StatusChangeEvent.INLINE_GRAPHIC_STATUS_CHANGE, _field.inlineGraphicStatusChangeHandler); if (!textContainerManager) textContainerManager = new FTETextFieldTextContainerManager(_field); textContainerManager.compositionWidth = compositionWidth; textContainerManager.compositionHeight = compositionHeight; textContainerManager.editingMode = EditingMode.READ_ONLY; textContainerManager.hostFormat = hostFormat; textContainerManager.swfContext = _field.fontContext as ISWFContext; textContainerManager.setTextFlow(textFlow) // Add a formatResolver if there is a style sheet. Force a flow // composer to be created, if there isn't one, so the format resolver // will be used. if (_styleSheet && !textFlow.formatResolver) { textFlow.formatResolver = new FTETextFieldStyleResolver(_styleSheet); textContainerManager.beginInteraction(); textContainerManager.endInteraction(); } textContainerManager.updateContainer(); var bounds:Rectangle = textContainerManager.getContentBounds(); _field._textWidth = Math.round(bounds.width); _field._textHeight = Math.round(bounds.height); // TLF takes care of clipping so none should be needed here. _field.clipWidth = _field._textWidth; } /** * @private */ private function linkClickHandler(event:FlowElementMouseEvent):void { // Need to remove the event: portion of the href if it has one. // Only dispatch the event if it has the event portion. var href:String = LinkElement(event.flowElement).href; if (href.indexOf("event:") == 0) { var textEvent:TextEvent = new TextEvent(TextEvent.LINK); textEvent.text = href.substring(6); _field.dispatchEvent(textEvent); } } /** * @private */ public function getHtmlText(fieldText:String):String { if (_htmlText == null) { // We can optimize the default case // that there is no text or hmtlText. if (fieldText == "") { _htmlText = ""; } else { // Import the plain text into a TextFlow, // and then export the TextFlow into HTML. if (!textFlow) textFlow = plainTextImporter.importToFlow(fieldText); _htmlText = String(htmlExporter.export( textFlow, ConversionType.STRING_TYPE)); } } return _htmlText; } /** * @private */ public function set styleSheet(value:StyleSheet):void { _styleSheet = value; var textFlow:TextFlow = textFlow; if (textFlow && textFlow.formatResolver) textFlow.formatResolver = null; } public function getText():String { var rslt:String; // If we don't already have a TextFlow, // create one by importing the htmlText. if (!textFlow) textFlow = htmlImporter.importToFlow(_htmlText); // Export plain text from the TextFlow. return String(plainTextExporter.export( textFlow, ConversionType.STRING_TYPE)); } }