//////////////////////////////////////////////////////////////////////////////// // // 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 UnitTest.Tests { import UnitTest.ExtendedClasses.TestSuiteExtended; import UnitTest.ExtendedClasses.VellumTestCase; import UnitTest.Fixtures.TestConfig; import UnitTest.Validation.MD5; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.DisplayObject; import flash.display.IBitmapDrawable; import flash.display.Sprite; import flash.events.*; import flash.filters.BitmapFilter; import flash.filters.BitmapFilterQuality; import flash.filters.BlurFilter; import flash.geom.Rectangle; import flash.system.*; import flash.text.engine.*; import flash.utils.ByteArray; import flashx.textLayout.*; import flashx.textLayout.compose.StandardFlowComposer; import flashx.textLayout.compose.TextLineRecycler; import flashx.textLayout.container.ContainerController; import flashx.textLayout.container.ScrollPolicy; import flashx.textLayout.container.TextContainerManager; import flashx.textLayout.conversion.TextConverter; import flashx.textLayout.edit.EditManager; import flashx.textLayout.edit.IEditManager; import flashx.textLayout.edit.ISelectionManager; import flashx.textLayout.edit.SelectionManager; import flashx.textLayout.elements.*; import flashx.textLayout.events.StatusChangeEvent; import flashx.textLayout.events.UpdateCompleteEvent; import flashx.textLayout.factory.StringTextLineFactory; import flashx.textLayout.factory.TextFlowTextLineFactory; import flashx.textLayout.formats.TextLayoutFormat; import mx.containers.Canvas; import mx.core.ByteArrayAsset; import mx.utils.LoaderUtil; use namespace tlf_internal; public class TextLineFilterTest extends VellumTestCase { private var displayobject:DisplayObject; private var TestCanvas:Canvas = null; private var ItemsToRemove:Array; private var text:String = 'There are many such lime-kilns in that tract of country. '; private var headingFlow:TextFlow; private var bodyFlow:TextFlow; private var headController:CustomContainerController; private var bodyController:CustomContainerController; private const bodyMarkup:String = "" + "" + "There are many " + "such" + " lime-kilns in that tract of country, for the purpose of burning the white" + " marble which composes a large part of the substance of the hills. Some of them, built " + "years ago, and long deserted, with weeds growing in the vacant round of the interior, " + "which is open to the sky, and grass and wild-flowers rooting themselves into the chinks " + "of the stones, look already like relics of antiquity, and may yet be overspread with the" + " lichens of centuries to come. Others, where the lime-burner still feeds his daily and " + "nightlong fire, afford points of interest to the wanderer among the hills, who seats " + "himself on a log of wood or a fragment of marble, to hold a chat with the solitary man. " + "It is a lonesome, and, when the character is inclined to thought, may be an intensely " + "thoughtful occupation; as it proved in the case of Ethan Brand, who had mused to such " + "strange purpose, in days gone by, while the fire in this very kiln was burning." + "" + "" + "" + "The man who now watched the fire was of a different order, and troubled himself with no " + "thoughts save the very few that were requisite to his business. At frequent intervals, " + "he flung back the clashing weight of the iron door, and, turning his face from the " + "insufferable glare, thrust in huge logs of oak, or stirred the immense brands with a " + "long pole. Within the furnace were seen the curling and riotous flames, and the burning " + "marble, almost molten with the intensity of heat; while without, the reflection of the " + "fire quivered on the dark intricacy of the surrounding forest, and showed in the " + "foreground a bright and ruddy little picture of the hut, the spring beside its door, the " + "athletic and coal-begrimed figure of the lime-burner, and the halffrightened child, " + "shrinking into the protection of his father's shadow. And when again the iron door was " + "closed, then reappeared the tender light of the half-full moon, which vainly strove to " + "trace out the indistinct shapes of the neighboring mountains; and, in the upper sky, " + "there was a flitting congregation of clouds, still faintly tinged with the rosy sunset, " + "though thus far down into the valley the sunshine had vanished long and long ago." + "" + ""; // embed alice - this simplifies things - don't need to trust the swf and pass the xml around with it [Embed(source="../../../../test/testFiles/markup/tlf/alice.xml",mimeType="application/octet-stream")] private var AliceClass : Class; public function TextLineFilterTest(methodName:String, testID:String, testConfig:TestConfig, testCaseXML:XML=null) { ItemsToRemove = new Array(); super(methodName, testID, testConfig, testCaseXML); //reset containerType and ID containerType = "custom"; // Note: These must correspond to a Watson product area (case-sensitive) metaData.productArea = "Text Container"; } public static function suiteFromXML(testListXML:XML, testConfig:TestConfig, ts:TestSuiteExtended):void { var testCaseClass:Class = TextLineFilterTest; VellumTestCase.suiteFromXML(testCaseClass, testListXML, testConfig, ts); } override public function setUp() : void { cleanUpTestApp(); TestDisplayObject = testApp.getDisplayObject(); if (TestDisplayObject) { TestCanvas = Canvas(TestDisplayObject); } else { fail ("Did not get a blank canvas to work with"); } } private var bodyContainer:Sprite; public function initApp():void { // create body text flow, import body text, and assign flow composer bodyFlow = new TextFlow(); bodyFlow = TextConverter.importToFlow(bodyMarkup, TextConverter.TEXT_LAYOUT_FORMAT); bodyFlow.flowComposer = new StandardFlowComposer(); bodyContainer = new Sprite(); bodyController = new CustomContainerController(bodyContainer, 800, 1000); bodyController.updateCompositionShapes(); //bodyController.addTextLine(tl1, 0); bodyContainer.x = 20; bodyContainer.y = 20; // create container format to specify columns var bodyContainerFormat:TextLayoutFormat = new TextLayoutFormat(); bodyContainerFormat.columnWidth = 390; bodyContainerFormat.columnGap = 15; bodyController.format = bodyContainerFormat; // enable scrolling bodyController.verticalScrollPolicy = ScrollPolicy.AUTO; } /** * Apply a filter to the text lines and the selection */ public function textlineAndSelectionFilterTest():void { initApp(); bodyFlow.interactionManager = new SelectionManager(); var selectManager:ISelectionManager = bodyFlow.interactionManager; selectManager.selectRange(0, 107); selectManager.refreshSelection(); // add controller, add container to stage, and display body text bodyFlow.flowComposer.addController(bodyController); bodyFlow.flowComposer.updateAllControllers(); TestCanvas.rawChildren.addChild(DisplayObject(bodyContainer)); ItemsToRemove.push(bodyContainer); System.gc(); //mark System.gc(); //sweep } public function getBitmapFilter():BitmapFilter { var blurX:Number = 3; var blurY:Number = 3; return new BlurFilter(blurX, blurY, BitmapFilterQuality.HIGH); } /** * Apply a filter to the text lines, but not the selection */ public function textlinesFilterTest():void { initApp(); bodyFlow.interactionManager = new SelectionManager(); var selectManager:ISelectionManager = bodyFlow.interactionManager; // add controller, add container to stage, and display body text bodyFlow.flowComposer.addController(bodyController); bodyFlow.flowComposer.updateAllControllers(); TestCanvas.rawChildren.addChild(DisplayObject(bodyContainer)); ItemsToRemove.push(bodyContainer); System.gc(); //mark System.gc(); //sweep } /** * Apply a filter to the selection, but not the textline */ public function selectionFilterTest():void { initApp(); bodyFlow.interactionManager = new SelectionManager(); var selectManager:ISelectionManager = bodyFlow.interactionManager; selectManager.selectRange(1000, 2000); selectManager.refreshSelection(); // add controller, add container to stage, and display body text bodyFlow.flowComposer.addController(bodyController); bodyFlow.flowComposer.updateAllControllers(); TestCanvas.rawChildren.addChild(DisplayObject(bodyContainer)); ItemsToRemove.push(bodyContainer); System.gc(); //mark System.gc(); //sweep } private var recycledTextLine:TextLine; private var composedTextLine:TextLine; private var _lines:Array; private var textLine_invalid:TextLine; private static function createInvalidTextLine():TextLine { var elementFormat:ElementFormat = new ElementFormat(); var textElement:TextElement = new TextElement("aaa", elementFormat) var textBlock:TextBlock = new TextBlock(textElement); var textLine:TextLine = textBlock.createTextLine(); textLine.validity = TextLineValidity.INVALID; return textLine; } public function recycleLineByFlowComposer():void { //create an invald TextLine to recycle textLine_invalid = createInvalidTextLine(); //recycle the invalid TextLine TextLineRecycler.emptyReusableLineCache(); TextLineRecycler.addLineForReuse(textLine_invalid); recycledTextLine = textLine_invalid; //create a TextFlow var textFlow:TextFlow = new TextFlow(); textFlow.mxmlChildren = [ "Hello, World" ]; var textContainer:Sprite = new Sprite(); var textController:ContainerController = new CustomContainerController(textContainer, 800, 1000); textFlow.flowComposer.addController(textController); //compose a single line which should be from recycler textFlow.flowComposer.updateAllControllers(); TestCanvas.rawChildren.addChild(DisplayObject(textContainer)); //check if the composed line is same as the recycled one composedTextLine = textFlow.flowComposer.getLineAt(0).getTextLine(); assertTrue("The composed line is not same as recycled line.", recycledTextLine === composedTextLine); } public function recycleLineByFlowFactory():void { _lines = new Array(); //create an invald TextLine to recycle textLine_invalid = createInvalidTextLine(); //recycle the invalid TextLine TextLineRecycler.emptyReusableLineCache(); TextLineRecycler.addLineForReuse(textLine_invalid); recycledTextLine = textLine_invalid; var factory:TextFlowTextLineFactory = new TextFlowTextLineFactory(); factory.compositionBounds = new Rectangle( 100, 100, 200, 130 ); var flow:TextFlow = new TextFlow(); var span:SpanElement = new SpanElement(); span.text = "Hello World."; var para:ParagraphElement = new ParagraphElement(); para.addChild( span ); flow.addChild( para ); //compose a single line which should be from recycler factory.createTextLines(useTextLines, flow ); //check if the composed line is same as the recycled one composedTextLine = _lines[0]; assertTrue("The composed line is not same as recycled line.", recycledTextLine === composedTextLine); } public function recycleLineByStringFactory():void { _lines = new Array(); //create an invald TextLine to recycle textLine_invalid = createInvalidTextLine(); //recycle the invalid TextLine TextLineRecycler.emptyReusableLineCache(); TextLineRecycler.addLineForReuse(textLine_invalid); recycledTextLine = textLine_invalid; var factory:StringTextLineFactory = new StringTextLineFactory(); factory.compositionBounds = new Rectangle( 100, 100, 200, 130 ); factory.text = "Hello World"; //compose a single line which should be from recycler factory.createTextLines(useTextLines); //check if the composed line is same as the recycled one composedTextLine = _lines[0]; assertTrue("The composed line is not same as recycled line.", recycledTextLine === composedTextLine); } public function stringFactoryBackgroundColor():void { _lines = new Array(); var factory:StringTextLineFactory = new StringTextLineFactory(); factory.compositionBounds = new Rectangle( 100, 100, 200, 130 ); factory.text = "Hello World"; factory.textFlowFormat = TextLayoutFormat.createTextLayoutFormat({blockProgression:writingDirection[0],direction:writingDirection[1]}); factory.spanFormat = TextLayoutFormat.createTextLayoutFormat({backgroundColor:0xffff4F,fontSize:48}); factory.createTextLines(useTextLines); } private function useTextLines( tl:DisplayObject ):void { _lines.push(tl); TestCanvas.rawChildren.addChild( tl ); } public function recycleLineByCustomLineCreatorTextFlow():void { //create an invald TextLine to recycle textLine_invalid = createInvalidTextLine(); //recycle the invalid TextLine TextLineRecycler.emptyReusableLineCache(); TextLineRecycler.addLineForReuse(textLine_invalid); recycledTextLine = textLine_invalid; //Create a TextFlow var textFlow:TextFlow = new TextFlow(); textFlow.mxmlChildren = [ "Hello, World" ]; var textContainer:Sprite = new Sprite(); var textController:ContainerController = new CustomContainerController(textContainer, 800, 1000); textFlow.flowComposer.addController(textController); //monitor if the TextLine created from recycler var myswfContext:MySwfContext = new MySwfContext() textFlow.flowComposer.swfContext = myswfContext; textFlow.flowComposer.updateAllControllers(); //check if composed line is same as recycled line and if createTextLine gets the recycled line composedTextLine = textFlow.flowComposer.getLineAt(0).getTextLine(); assertTrue("The composed line is not same as recycled line.", recycledTextLine === composedTextLine && myswfContext.numRecycledLines == 1); //monitor if the TextLine created from new textFlow.mxmlChildren = [ "Again, Hello, World " ]; textFlow.flowComposer.swfContext = myswfContext; textFlow.flowComposer.updateAllControllers(); assertTrue("The composed line is not newly created", myswfContext.numLinesCreated == 1); TestCanvas.rawChildren.addChild(DisplayObject(textContainer)); } public function recycleLineByTCM():void { //create an invald TextLine to recycle textLine_invalid = createInvalidTextLine(); //recycle the invalid TextLine TextLineRecycler.emptyReusableLineCache(); TextLineRecycler.addLineForReuse(textLine_invalid); recycledTextLine = textLine_invalid; //create a TextFlow by TCM using the recycled line var sprite:Sprite = new Sprite(); sprite.x = 50; sprite.y = 50; var tcm:TextContainerManager = new TextContainerManager(sprite); tcm.setText("Hello World"); tcm.updateContainer(); var editManager:EditManager = EditManager(tcm.beginInteraction()) editManager.selectRange(0, 0); var textFlow:TextFlow = tcm.getTextFlow(); //check if the composed line is same as the recycled one composedTextLine = textFlow.flowComposer.getLineAt(0).getTextLine(); assertTrue("The composed line is not same as recycled line.", recycledTextLine === composedTextLine); TestCanvas.rawChildren.addChild(DisplayObject(sprite)); TestCanvas.rawChildren.addChild(sprite); } public function recycleLineByCustomLineCreatorFlowFactory():void { _lines = new Array(); //create an invald TextLine to recycle textLine_invalid = createInvalidTextLine(); //recycle the invalid TextLine TextLineRecycler.emptyReusableLineCache(); TextLineRecycler.addLineForReuse(textLine_invalid); recycledTextLine = textLine_invalid; var factory:TextFlowTextLineFactory = new TextFlowTextLineFactory(); factory.compositionBounds = new Rectangle( 100, 100, 200, 130 ); //Create a TextFlow var flow:TextFlow = new TextFlow(); flow.mxmlChildren = [ "Hello, World" ]; //create custom TextLine creator var myswfContext:MySwfContext = new MySwfContext() flow.flowComposer.swfContext = myswfContext; factory.swfContext = flow.flowComposer.swfContext; //compose a single line which should be from recycler using FlowFactory factory.createTextLines(useTextLines, flow ); //check if the composed line is same as the recycled one composedTextLine = _lines[0]; assertTrue("The composed line is not same as recycled line.", recycledTextLine === composedTextLine && myswfContext.numRecycledLines == 1); //monitor if the TextLine created from new flow.mxmlChildren = [ "Again, Hello, World " ]; factory.compositionBounds = new Rectangle( 100, 200, 200, 130 ); factory.createTextLines(useTextLines, flow ); assertTrue("The composed line is not newly created", myswfContext.numLinesCreated == 1); } public function recycleLineByCustomLineCreatorStringFactory():void { _lines = new Array(); //create an invald TextLine to recycle textLine_invalid = createInvalidTextLine(); //recycle the invalid TextLine TextLineRecycler.emptyReusableLineCache(); TextLineRecycler.addLineForReuse(textLine_invalid); recycledTextLine = textLine_invalid; var factory:StringTextLineFactory = new StringTextLineFactory(); factory.compositionBounds = new Rectangle( 100, 100, 200, 130 ); factory.text = "Hello World"; //create custom TextLine creator var myswfContext:MySwfContext = new MySwfContext() factory.swfContext = myswfContext; //compose a single line which should be from recycler using FlowFactory factory.createTextLines(useTextLines); //check if the composed line is same as the recycled one composedTextLine = _lines[0]; assertTrue("The composed line is not same as recycled line.", recycledTextLine === composedTextLine && myswfContext.numRecycledLines == 1); //monitor if the TextLine created from new factory.text = "Again, Hello World"; factory.compositionBounds = new Rectangle( 100, 200, 200, 130 ); factory.createTextLines(useTextLines); assertTrue("The composed line is not newly created", myswfContext.numLinesCreated == 1); } public function recycleLineByCustomLineCreatorTCM():void { _lines = new Array(); //create an invald TextLine to recycle textLine_invalid = createInvalidTextLine(); //recycle the invalid TextLine TextLineRecycler.emptyReusableLineCache(); TextLineRecycler.addLineForReuse(textLine_invalid); recycledTextLine = textLine_invalid; //create a TextFlow by TCM using the recycled line var sprite:Sprite = new Sprite(); sprite.x = 50; sprite.y = 50; var tcm:TextContainerManager = new TextContainerManager(sprite); tcm.setText("Hello World"); tcm.updateContainer(); var editManager:EditManager = EditManager(tcm.beginInteraction()); editManager.selectRange(0, 0); var textFlow:TextFlow = tcm.getTextFlow(); //check if the composed line is same as the recycled one composedTextLine = textFlow.flowComposer.getLineAt(0).getTextLine(); assertTrue("The composed line is not same as recycled line.", recycledTextLine === composedTextLine); TestCanvas.rawChildren.addChild(sprite); } public function recycleLineInSuite():void { var alice:ByteArrayAsset = new AliceClass(); var aliceData:String = alice.readMultiByte(alice.length,"utf-8"); var textFlow:TextFlow = TextConverter.importToFlow(aliceData, TextConverter.TEXT_LAYOUT_FORMAT); TextLineRecycler.emptyReusableLineCache(); TextLineRecycler.textLineRecyclerEnabled = false; var s:Sprite = new Sprite(); s.x = 50; s.y = 50; var tcm:TextContainerManager = new TextContainerManager(s); tcm.compositionWidth = 500; tcm.compositionHeight = NaN; tcm.setTextFlow(textFlow); tcm.updateContainer(); displayobject = testApp.getDisplayObject(); var bits:BitmapData = new BitmapData(displayobject.width, displayobject.height); bits.draw(TestDisplayObject as IBitmapDrawable); bitmapSnapshot = new Bitmap(bits); bits = null; var pixels:ByteArray = bitmapSnapshot.bitmapData.getPixels(bitmapSnapshot.bitmapData.rect); pixels.compress(); pixels.position = 0; var checksumRecyclingOff:String = MD5.hashBinary(pixels); //create a bunch(100) of invald TextLines to recycle TextLineRecycler.emptyReusableLineCache(); TextLineRecycler.textLineRecyclerEnabled = true; var i:int = 0; while (i<=100) { var textLine_invalid:TextLine = createInvalidTextLine(); TextLineRecycler.addLineForReuse(textLine_invalid); i++; } //recompose the text flow with the recycled lines tcm.updateContainer(); //get checksum for recycling on var bits_2:BitmapData = new BitmapData(displayobject.width, displayobject.height); bits_2.draw(TestDisplayObject as IBitmapDrawable); bitmapSnapshot = new Bitmap(bits_2); bits_2 = null; pixels = bitmapSnapshot.bitmapData.getPixels(bitmapSnapshot.bitmapData.rect); pixels.compress(); pixels.position = 0; var checksumRecyclingOn:String = MD5.hashBinary(pixels); //check if there is any rendering difference assertTrue("the rendering changed between recycle on and off.", checksumRecyclingOn == checksumRecyclingOff); TestCanvas.rawChildren.addChild(DisplayObject(s)); } private var tf:TextFlow = new TextFlow(); public function embeddedInlineGraphics(callback:Object = null):void { if(!callback) { callback = true; tf.addEventListener(StatusChangeEvent.INLINE_GRAPHIC_STATUS_CHANGE,addAsync(embeddedInlineGraphics,2500,null),false,0,true); var TestCanvas:Canvas; TestDisplayObject = testApp.getDisplayObject(); if (TestDisplayObject) { TestCanvas = Canvas(TestDisplayObject); } var container:Sprite = new Sprite(); TestCanvas.rawChildren.addChild(container); var p:ParagraphElement = new ParagraphElement(); var span:SpanElement = new SpanElement(); // add text to the span span.text = "AAAAAAAA"; p.addChild(span); tf.addChild(p); // update controller to display text var controller:ContainerController = new CustomContainerController(container, 400, 200); tf.flowComposer.addController(controller); tf.flowComposer.updateAllControllers(); var src:String = "smiley.gif"; var width:int = 20; var height:int = 20; var baseImageURL:String = LoaderUtil.createAbsoluteURL(baseURL, "../../test/testFiles/assets/"); var selectManager:IEditManager = new EditManager(); tf.interactionManager = selectManager; selectManager.setFocus(); selectManager.selectRange(3, 3); var inlineGraphicElement:InlineGraphicElement = selectManager.insertInlineGraphic(LoaderUtil.createAbsoluteURL(baseImageURL,src),width,height); tf.flowComposer.updateAllControllers(); } else // Make sure the image is ready before we let the snapshot go { var img:InlineGraphicElement = tf.findLeaf(3) as InlineGraphicElement; if(img.status != InlineGraphicElementStatus.READY) { tf.addEventListener(StatusChangeEvent.INLINE_GRAPHIC_STATUS_CHANGE,addAsync(embeddedInlineGraphics,2500,null),false,0,true); } } } } } import flash.display.BlendMode; import flash.display.Sprite; import flash.display.Shape; import flash.display.DisplayObjectContainer; import flash.text.engine.TextLine; import flashx.textLayout.container.ContainerController; import flash.filters.*; import flashx.textLayout.tlf_internal; import flash.display.DisplayObject; use namespace tlf_internal; class CustomContainerController extends ContainerController { public function CustomContainerController(container:Sprite,compositionWidth:Number=100,compositionHeight:Number=100) { super(container,compositionWidth,compositionHeight); // We'll put all text lines in a nested container // Filters applied to this container won't affect other objects like selection or background shapes _textLineContainer = new Sprite(); //_textLineContainer.filters = [ new DropShadowFilter() ]; // apply a filter (for illustration) container.addChild(_textLineContainer); } // Return the index where the first text line should appear in its parent // Since our _textLineContainer only holds text lines, return zero protected override function getFirstTextLineChildIndex():int { return 0; } // Add a text line at the specified index protected override function addTextLine(textLine:TextLine, index:int):void { if(index == 0) textLine.filters = [ new BlurFilter(3, 3, BitmapFilterQuality.HIGH) ]; _textLineContainer.addChildAt(textLine, index); } protected override function addInlineGraphicElement(parent:DisplayObjectContainer, inlineGraphicElement:DisplayObject, index:int):void { parent.addChildAt(inlineGraphicElement, index); } // Remove a text line, but only if it is in _textLineContainer // (A text line may have moved to another container because of 'shuffling') protected override function removeTextLine(textLine:TextLine):void { if (_textLineContainer.contains(textLine)) { textLine.filters = null; _textLineContainer.removeChild(textLine); } } // Add the container for selection shapes (block or point selection) protected override function addSelectionContainer(selectionContainer:DisplayObjectContainer):void { // If selection container is opaque or has normal blend mode, put selection container behind the text lines, else in front var filter:BlurFilter = new BlurFilter(10, 10, BitmapFilterQuality.HIGH); var myFilters:Array = new Array(); myFilters.push(filter); selectionContainer.filters = myFilters; var index:int = selectionContainer.blendMode == BlendMode.NORMAL && selectionContainer.alpha == 1 ? container.getChildIndex(_textLineContainer) : container.numChildren; container.addChildAt(selectionContainer, index); } // Remove the container for selection shapes protected override function removeSelectionContainer(selectionContainer:DisplayObjectContainer):void { container.removeChild(selectionContainer); } // Add the background shape protected override function addBackgroundShape(shape:Shape):void { container.addChildAt(shape, 0); // behind everything else, so use index 0 } private var _textLineContainer:Sprite; } import flashx.textLayout.formats.TextLayoutFormat; import flashx.textLayout.compose.ISWFContext; import flash.text.engine.TextBlock; import flashx.textLayout.elements.Configuration; class MySwfContext implements ISWFContext { public var numLinesCreated:int = 0; public var numRecycledLines:int = 0; public function callInContext(fn:Function, thisArg:Object, argsArray:Array, returns:Boolean=true):* { var textBlock:TextBlock = thisArg as TextBlock; if (textBlock) { if (fn == textBlock.createTextLine) { numLinesCreated++; // COUNT: create a new TextLine } else if (Configuration.playerEnablesArgoFeatures && fn == thisArg["recreateTextLine"]) { numRecycledLines++; // COUNT: make a TextLine using a recycled line } } if (returns) return fn.apply(thisArg, argsArray); fn.apply(thisArg, argsArray); } }