Loader Component and Internet Explorer

On a recent project, I found an oddity with the loader component.  I found some images loaded with it were having their complete event fired before the image was actually done loading.  This caused all sorts of problems, as I was dynamically resizing a frame around the loaded content in the complete event.  With the event firing prematurely, the height and width of the content were showing as 0,0 so the frame was not being properly sized.  It seems the issue lies with the mx.core.ExternalContent class which is used by mx.core.View (the superclass of Loader).

ExternalContent has a method checkLoadProgress which reads like this:

function checkLoadProgress():Void
 {
  var i:String;
  for (i in loadList)
  {
   var x:Object = loadList[i];
   //trace("loading…" + i);
   x.loaded = x.obj.getBytesLoaded();
   x.total = x.obj.getBytesTotal();
   //trace( x.loaded +"/"+ x.total );
   if (x.total > 0)
   {
    x.obj._visible = false;
    dispatchEvent({type: "progress", target: x.obj, current: x.loaded, total: x.total});
    if (x.loaded == x.total) {
     if (loadedList == undefined)
      loadedList = new Object();
     loadedList[i] = x;
     delete loadList[i];
     doLater(this, "contentLoaded");
    }
   }
   else if (x.total == -1)
   {
    // sometimes you get a -1 before it starts loading
    if (x.failedOnce != undefined)
    {
     x.failedOnce++;
     if (x.failedOnce > 3)
     {
      dispatchEvent({type: "complete", target: x.obj, current: x.loaded, total: x.total});
      //trace("total == -1 loaded = " + x.loaded);
      delete loadList[i];
      delete x;
     }
    }
    else
     x.failedOnce = 0;
   }
   else
   {
   }
   doLater(this, "checkLoadProgress");
  }
 }

The problem is the line "if (x.failedOnce > 3)"  whats happening here, is that if after 3 tries the content still hasn’t started loaded (therefore the getBytesLoaded() call returns -1), this component fires its complete event, even though it hasnt actually started.  I dont know why this is being done, but clearly, its causing problems.     

There are several possible solutions, including one mentioned on bgxcomponents.com, which uses the prototype method to override the component.

Another less desirable solution would be to change the code in the ExternalContnet class.  this is clearly a bad solution, as this code was probably written for a reason, and just because we dont understand why, doesnt mean its not valid for some cases. 

The OO friendly solution would be to subclass Loader, and override the method for our new subclass.  I’ve done this, the code can be seen below.

class IELoader extends mx.controls.Loader{
  static var symbolName:String = "IELoader";
  static var symbolOwner:Object = IELoader;
  var className:String = "IELoader";
  var loadList:Object;
  var loadedList:Object;

  function checkLoadProgress(){
  
   for (var i in this.loadList){
     var x:Object = this.loadList[i];
   
     x.loaded = x.obj.getBytesLoaded();
     x.total = x.obj.getBytesTotal();
     
     if (x.total > 0){
       x.obj._visible = false;
       this.dispatchEvent({type: "progress", target: x.obj, current: x.loaded, total: x.total});
       if (x.loaded == x.total) {
         if (this.loadedList == undefined){
           this.loadedList = new Object();
         }
         this.loadedList[i] = x;
         delete loadList[i];
         this.doLater(this, "contentLoaded");
       } else {
         this.doLater(this, "checkLoadProgress");
       }
     } else {
       if (x.total == -1){
         if (x.failedOnce != undefined){
           x.failedOnce++;
         } else {
           x.failedOnce = 0;
         }
       }
       this.doLater(this, "checkLoadProgress");
     }
   }
 }
}

Remember, this is a class file for a new Component.  To effectively use it, it needs to be linked with a movie clip, which will act as your component.  For ease of use, I’ve uploaded an installer which contains this class packaged as a Flash component.  You can download it from here.

There are no comments.

Leave a Reply