Handling image loading in TLF TextFlow and TextField

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();
        }
}

One thought on “Handling image loading in TLF TextFlow and TextField