Recently, I was building a cms driven (Drupal 6 to be precise) website and faced the problem of detecting whether the content of TextField or TFL TextFlow has completed loading.
This example is based on TLF Editor from Tour de Flex, which I used as a base for the created editor/display component.
1. TLF TextFlow solution
The solution I used is actually very simple.
First, you need to analyze the TextFlow markup to get the number of images in text.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //total images counter private var totalImages:uint = 0; //number of loaded images private var imagesLoaded:uint = 0; private function initTextFlowImagesCounter(textFlowMarkup:String):void { //<img source="http://image_url.jpg" /> var source_AttributeRegExp:RegExp = /source="[^"]+"/gi; var result:Object = source_AttributeRegExp.exec(textFlowMarkup); while (result != null) { var image_source:String = String(result); totalImages++; result = source_AttributeRegExp.exec(textFlowMarkup); } } |
Then, create a TextFlow instance and setup the necessary listeners.
1 2 3 | initTextFlowImagesCounter(markup); var activeFlow:TextFlow = TextConverter.importToFlow(markup, TextConverter.TEXT_LAYOUT_FORMAT); activeFlow.addEventListener(StatusChangeEvent.INLINE_GRAPHIC_STATUS_CHANGE,recomposeOnLoadComplete,false,0,true); |
We handle all StatusChangeEvent.INLINE_GRAPHIC_STATUS_CHANGE events in recomposeOnLoadComplete function.
Basicaly, this function updates the ‘imagesLoaded’ counter, but there are few things worth mentioning.
The whole image loading process in TLF is quite different than flash TextField approach, we need to manually handle the states of image elements.
When the status is InlineGraphicElementStatus.SIZE_PENDING then the image has finished loaded, but the TLF didn’t actually render it, because the controller didn’t know the dimensions of the image – so we need to call activeFlow.flowComposer.updateAllControllers(). This situation will happen only when an tag doesn’t have declared ‘width’ and ‘height’ explicitly to a number.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | private function recomposeOnLoadComplete(e:StatusChangeEvent):void { //image load error if (e.status == InlineGraphicElementStatus.ERROR) { imagesLoaded--; } if (e.element.getTextFlow() == activeFlow) { //image has loaded - we need to recalculate the size of the TLF container if (e.status == InlineGraphicElementStatus.SIZE_PENDING) { activeFlow.flowComposer.updateAllControllers(); } else //image has been loaded and updated if (e.status == InlineGraphicElementStatus.READY) { imagesLoaded++; } checkIfLoaded(); } } private function checkIfLoaded():void { //all images loaded if (totalImages == imagesLoaded) { updateSizeInfo();//just an option dispatchEvent(new Event(Event.COMPLETE)); } } |
The content displayed on the website has a lot of images, so need to know the height of the TextFlow object to be able to scroll it.
This is done by this handy function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | private function updateSizeInfo():int { if (activeFlow) { var cont:ContainerController = activeFlow.flowComposer.getControllerAt(0); if (cont.container) { //height of the TextFlow instance var textHeight:int = Math.ceil(cont.getContentBounds().height); return textHeight; } } return 0; } |
2. TextField solution
The whole idea is the same, but we need to setup a Loader instance for each image to handle errors and load events. We can get the height of TextField content from the textHeight attribute.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | private function initTextFieldImagesLoading(htmlText:String):void { //search for the 'id' attribute of 'img' tag var idAttributeRegExp:RegExp = /id="[^"]+"/gi; var result:Object = idAttributeRegExp.exec(htmlText); while (result != null) { var imageId:String = String(result); imageId = imageId.replace('id="', ''); imageId = imageId.replace('"', ''); //create an Loader instance for each image - txtDescription is our TextField with htmlText var ldr:Loader = txtDescription.getImageReference(imageId) as Loader; if (ldr != null) { totalImages++; ldr.contentLoaderInfo.addEventListener(Event.INIT, onTextFieldImageLoaded); ldr.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onTextFieldIOError); } result = idAttributeRegExp.exec(htmlText); } private function onTextFieldImageLoaded(e:Event):void { e.target.removeEventListener(Event.INIT, onTextFieldImageLoaded); e.target.removeEventListener(IOErrorEvent.IO_ERROR, onTextFieldIOError); imagesLoaded++; checkIfLoaded(); } private function onTextFieldIOError(e:IOErrorEvent):void { e.target.removeEventListener(Event.INIT, onTextFieldImageLoaded); e.target.removeEventListener(IOErrorEvent.IO_ERROR, onTextFieldIOError); totalImages--; checkIfLoaded(); } } |