I have been endeavouring to understand the mysteries of LoadPanel ...

Attached is a commented version with some explanation of how things work and
some questions at some points as to what is being done and for what reasons.

I am trying to deal with a replicable case where 3 LoadPanels are updated at
once, from a slow server, but if a user closes one of the windows during the
process the LoadQueue hangs.

I have a WindowManager that is supposed to deal with this, by rebuilding the
Queue from valid remaining/created LoadPanels, when a LoadPanel is closed,
but I need to improve on it and so need to get to grips with how the
LoadPanel works.

I am open to any ideas as to how to make this more robust!

I am aware or the alternate methods to LoadPanel, and if I can quantify why
LoadPanel just can't handle this particularly application, I might be able
to pursuade the client to move to a remote scripting/java solution.

I have also produced some documentation for inclusion on the site - I have
entered it into the SF DocManager.

Cheers guys'n'gals

Liam
-- 
"Everything in moderation ... including moderation"

t/m: +44 (0)7050 367474
Im: metafeather

pgp on request

/*
   DynAPI Distribution
   LoadPanel Widget

   The DynAPI Distribution is distributed under the terms of the GNU LGPL license.

   Requirements:
        dynapi.api [dynlayer, dyndocument, browser, events]
        
        This enables a DynLayer to load external HTML files.
        
*/
function LoadPanel(url) {
        /*
        setup properties for the LoadPanel based on parent DynLayer
        */
        // ref to Parent?
        this.DynLayer = DynLayer;
        // create new empty DynLayer (fromdynlayer.js)
        this.DynLayer();
        // Boolean property used to enable the LoadPanel to change its height to match 
the content after loading
        this.autoH=true;
        // Boolean property used to enable the LoadPanel to change its width to match 
the content after loading
        this.autoW=false;
        // Boolean property used where?
        this.isILayer=false;
        // Boolean property used where? Based on Browser detect (from browser.js) to 
switch document.write output that actually gets the url
        this.isIFrame=!(is.ie5 && is.platform=='win32');
        /*
        Create event listeners for LoadPanel - onresize and oncreate
        */
        // create new eventlistener (from events.js)
        var l=new EventListener(this);
        l.onresize=function(e) {
                // create ref to LoadPanel clicked on (from dragevent.js)
                var o=e.getTarget();
                /*
                check properties of the LoadPanel
                */
                // if the LoadPanel does not exist or is already reloading then exit
                if (!o.created || o.isReloading) return;
                // if the LoadPanel has loaded and is meant to resize its height then 
reload
                if (o.autoH && o.url) o.reload();
        };
        l.oncreate=function(e) {
                var o=e.getTarget();
                // after the LoadPanel is formed load the url
                o.setURL(o.url);
                // Liam - something to do with the reloading mech?
                if (!o.isReloading && o.tempURL) {
                        o.setURL(o.tempURL);
                        delete o.tempURL;
                }
        };
        // add the listener we just set up (from dynlayer.js)
        this.addEventListener(l);
        // Liam - something to do with the reloading mech?
        this.tempURL=url;
        return this;
};
/*
        Contructor for the LoadPanel inheriting from DynLayer (from dynlayer.js)
*/
LoadPanel.prototype=new DynLayer();
/*
        Enables/Disables the LoadPanel ability to auto size its width to the content 
loaded (as interpreted by the browser)
*/
LoadPanel.prototype.setAutoResizeWidth=function(b) {
        this.autoW=b;
};
/*
        Enables/Disables the LoadPanel ability to auto size its height to the content 
loaded (as interpreted by the browser)
*/
LoadPanel.prototype.setAutoResizeHeight=function(b) {
        this.autoH=b;
};
/*
        Enables/Disables the LoadPanel method for loading external content for 
Netscape 4+ using a ILayer
*/
LoadPanel.prototype.useILayer=function(b) {
        if (is.ns4) {
                this.isILayer=b;
                // if the LoadPanel exists reload using the method set
                if (this.created) this.reload();
        }
};
/*
        Enables/Disables the LoadPanel method for loading external content for IE and 
DOM browsers using an IFrame
*/
LoadPanel.prototype.useIFrame=function(b) {
        if (is.ie || is.dom) {
                this.isIFrame=b;
                // if the LoadPanel exists reload using the method set          
                if (this.created) this.reload();
        }
};
/*
        Provides the mechanism of loading the external content by writing in HTML to 
load the content using native browser means (IFrame or ILAYER)
*/
LoadPanel.prototype.insertInlineElements=function() {
        if (is.ie) {
                // write the HTML into the LoadPanel
                if (this.isIFrame) this.setHTML('<IFRAME ID="'+this.id+'loadElement" 
STYLE="visibility: hidden; display: none;"></IFRAME>',false);
                else this.setHTML('<DIV ID="'+this.id+'loadElement" 
STYLE="behavior:url(#default#download)" style="display: none;"></DIV>',false);
        }
        // Liam - does this do anythingfor NN browsers?
        else if (is.ns4 && this.isILayer) this.setHTML('<ilayer></ilayer>',false);
        else if (is.ns6) this.setHTML('<IFRAME ID="'+this.id+'loadElement" 
STYLE="visibility: hidden;"></IFRAME>',false);
};
/*
        Create easy references to the above created IFrame/ILayers for use by the 
LoadPanel methods
*/
LoadPanel.prototype.findInlineElements=function() {
        if (is.ie) {
                // find the layer contained in the LoadPanel from the global browser 
arrays
                if (this.isIFrame) 
this.loadElement=document.frames(this.id+'loadElement');
                else this.loadElement=document.all(this.id+'loadElement');
        }
        else if (is.ns4) {
                // find the layer contained in the LoadPanel from the nested documents 
arrays
                if (this.isILayer) this.loadElement=this.doc.layers[0];
                // Liam - what is elm? (see dyndocument.js?)
                else this.loadElement=this.elm;
        }
        // find the layer contained in the LoadPanel from the global browser DOM
        else if (is.ns6) 
this.loadElement=document.getElementById(this.id+'loadElement');
};
/*
        Liam  - what does this do?
*/
LoadPanel.prototype.getFileScope=function() {
        if (!this.loadElement) return null;
        return this.loadElement;
};
/*
        Empty the LoadPanel
*/
LoadPanel.prototype.clearFile=function() {
        // set properties to null
        this.url=null;
        // clear the visible HTML code
        if (this.isILayer) {
                this.loadElement.document.write('');
                this.loadElement.document.close();
        }
        // reload the LoadPanel with no content
        else this.reload();
};
/*
        Get the currently loaded URL of the LoadPanel
*/
LoadPanel.prototype.getURL=function() {
        return this.url;
};
/*
        Set the URL of the LoadPanel
*/
LoadPanel.prototype.setURL=function(url) {
        // if no url supplied exit
        if (!url) return;
        // if the LoadPanel does not exist, create it and load the URL
        // Liam - how does the creation mechanism get called, and does the URL end up 
in the LoadQueue? 
        if (!this.created) this.url=url;
        // if the LoadPanel exists, add to the LoadQueue
        // Liam - good point to add ref to WindowManager array?
        else LoadPanel.queue.add(url,this);
};
/*
        Reload the LoadPanel
*/
LoadPanel.prototype.reload=function() {
        // create referenceable boolean to show LoadPanel is in process of reloading
        this.isReloading=true;
        var url=this.url;
        var p=this.parent;
        // delete the LoadPanel from its parent DynLayer (from dynlayer.js)
        this.removeFromParent();
        // empty the HTML of the parent DynLayer
        this.html = '';
        // recreate the LoadPanel
        // Liam - does the ID of the LoadPanel change because of this?
        p.addChild(this);
        // clear the boolean
        this.isReloading=false;
};
/*
        Do actions after loading the external content using the native browser method, 
but before transfering into the LoadPanel
*/
LoadPanel.prototype.loadHandler=function(url) {
        this.url=url;
        // for IE5 get a list of the imgs in the external content and set each state 
so we know when they are loaded
        // Liam - why, to do with intermittent visibility?
        if (is.ie5 && !this.isIFrame && this.elm && this.elm.all) {
                var imgs = this.elm.all.tags("img");
                for (var i=0;i<imgs.length;i++) {
                        if (imgs[i].readyState == 'uninitialized') imgs[i].src = 
imgs[i].src;
                }
        }
        if (is.ns4 && this.isILayer) {
                // get the width/height of the loaded document
                var w=this.loadElement.document.width;
                var h=this.loadElement.document.height;
        }
        else {
                // get the width/height of the loaded document (from dynlayer.js)      
 
                var w=this.getContentWidth();
                var h=this.getContentHeight();
        }
        // use the width/height vars to set the LoadPanel depending on desired 
behaviour
        if (this.autoW) this.setWidth(w,false);
        if (this.autoH) this.setHeight(h,false);
        // clear the reloader
        // Liam - why here?
        this.isReloading=false;
        // kick off an event to say the content has finished Loading
        // Liam - where is this caught?
        this.invokeEvent('load');
};

/* =============================== internal methods =============================== */

/*
        Create an array to keep track of the urls to be loaded into each LoadPanel - 
browsers only load one at time. 
*/
// Liam - An index is used to perform one action at a time?
function LoadQueue() {
        // create the array and call it 'queue'
        this.queue=new Array();
        // Liam - create a property to ref against the arrays index?
        this.index=0;
};
/*
        Output the contents of the LoadQueue as text
*/
// Liam - used for debugging, or below?
LoadQueue.prototype.toString=function() {
        return "LoadPanel.queue";
};
/*
        Add a url and the ref LoadPanel to the Queue
*/
LoadQueue.prototype.add=function(url,loadpanel) {
        // get the no of urls in the queue
        var q=this.queue.length;
        // add the url and LoadPanel ref into a 2-dimensional array 
        this.queue[q]=[url,loadpanel];
        // load the next url - may not be the one added.
        this.loadNext();
};
/*
        Load the next url in the Queue to its LoadPanel
*/
LoadQueue.prototype.loadNext=function() {
        // check the queue has been setup and isn't doing something
        if (!this.busy && this.queue[this.index]) {
                // set the state to busy
                this.busy=true;
                /*
                set a cascade of vars based on 2-dimensional array values
                */
                // ref the LoadPanel to be targeted by this Queue action
                var lpanel=this.currentLoadPanel=this.queue[this.index][1];
                // ref the url to be got by this Queue action
                var url=this.currentURL=this.queue[this.index][0];
                if (is.ns4) {
                        // create the browser native method to get the content
                        if (is.ILayer) lpanel.insertInlineElements();
                        // setup the ref to the browser native method
                        lpanel.findInlineElements();
                        // Liam - Clear all existing events (from events.js?)
                        DynAPI.document.releaseMouseEvents();
                        //
                        var lyr=lpanel.elm;
                        // Liam - makesure that the LoadPanel is not a Browser window? 
Makes sense I suppose
                        while(lyr.parentLayer!=window) lyr=lyr.parentLayer;
                        // Liam - Why cross reference these? Events set to call 
loadHandler when loaded or loaded true when loadHandler returns?
                        lyr.onload=LoadQueue.loadHandler;
                        lpanel.loadElement.onload=LoadQueue.loadHandler;
                        // load the url into the browser native method
                        lpanel.loadElement.src=url;
                } else {
                        // if we havent already...
                        if (!lpanel.loadElement) {
                                // create the browser native method to get the content 
                 
                                lpanel.insertInlineElements();
                                // setup the ref to the browser native method
                                lpanel.findInlineElements();
                        }
                        if (is.ie) {
                                if (lpanel.isIFrame) {
                                        // set state to loading
                                        lpanel.loadElement.document.isLoading=true;
                                        // load the url into the browser native method 
                                 
                                        lpanel.loadElement.location=url;
                                        // ??
                                        lpanel.timerID=setInterval(this.toString() + 
'.loadTimer()',250);
                                }
                                // Liam - where is the startDownload method declared?
                                else 
lpanel.loadElement.startDownload(url,LoadQueue.loadHandler);
                        }
                        else if (is.ns6) {
                                // ??
                                lpanel.timerID=setInterval(this.toString() + 
'.loadTimer()',250);
                                // load the url into the browser native method         
                         
                                lpanel.loadElement.src=url;
                        }
                }
                // after loading the content delete references to it from the Queue
                DynAPI.removeFromArray(this.queue,this.index);
        }
};
/*
        Create Load timers to check while the content is loading
*/
if (is.ns6) {
        LoadQueue.prototype.loadTimer=function() {
                // create ref to the current LoadPanel
                var lpanel=this.currentLoadPanel;
                // if the LoadPanel doesnt contain a native browser method - ie its 
been replaced with something else
                if (!document.getElementById(lpanel.id+'loadElement')) {
                        // clear the LoadPanels timer
                        clearInterval(lpanel.timerID);
                        // goto the next url in the Queue
                        LoadQueue.continueLoad();
                }
                // check to see if the content of the external document has been 
copied to the LoadPanel
                else if (lpanel.loadElement.contentDocument && 
lpanel.loadElement.contentDocument.body.innerHTML != document.body.innerHTML) {
                        // clear the LoadPanels timer
                        clearInterval(lpanel.timerID);
                        // Tell the loadHandler to copy the external document to the 
LoadPanel
                        LoadQueue.loadHandler(lpanel.loadElement.contentDocument);
                }
        }
} else if (is.ie) {
        LoadQueue.prototype.loadTimer=function() {
                // create ref to the current LoadPanel
                var lpanel=this.currentLoadPanel;
                // if the LoadPanel doesnt contain a native browser method - ie its 
been replaced with something else
                if (!document.frames(lpanel.id+'loadElement')) {
                        // clear the LoadPanels timer
                        clearInterval(lpanel.timerID);
                        // goto the next url in the Queue
                        LoadQueue.continueLoad();
                }
                // check to see if the content of the external document has been 
downloaded
                else if (!lpanel.loadElement.document.isLoading && 
(lpanel.loadElement.document.readyState=='interactive' || 
lpanel.loadElement.document.readyState=='complete')) {
                        // clear the LoadPanels timer
                        clearInterval(lpanel.timerID);
                        // Tell the loadHandler to copy the external document to the 
LoadPanel
                        
LoadQueue.loadHandler(lpanel.loadElement.document.body.innerHTML);
                }
        }
}
/*
        Handles the transfer of HTML code from the hidden native browser method to the 
visible LoadPanel
*/
// Liam - I dont know if 'e' in this case is an event object?
LoadQueue.loadHandler=function(e) {
        // set ref to the whole Queue array
        var q=LoadPanel.queue;
        // find out the currentLoadPanel
        var lp=q.currentLoadPanel;
        // if we can find the LoadPanel
        if (q.currentLoadPanel) {
                if (is.ie) {
                        // The following is used in IE4 on E4.com, not standard DynAPI 
code!
                        // Liam - I have no idea - something to do with events??? 
Can't find anything else like this in either API
                        if (is.ie4) {
                                e = e.replace(/\<\!\-\-\_\_IE4PARAM\_\_/g,'');
                                e = e.replace(/\_\_IE4PARAM\_\_\-\-\>/g,'');
                        }
                        // set the innerHTML of the LoadPanel to the external content 
(clearing the native browser method)
                        lp.elm.innerHTML=e;
                        // if an IFrame explicitly delete the native browser method
                        if (lp.isIFrame) lp.loadElement=null;
                }
                else if (is.ns4) {
                        var lyr = lp.elm;
                        // Liam - makesure that the LoadPanel is not a Browser window? 
Makes sense I suppose
                        while(lyr.parentLayer != window) lyr = lyr.parentLayer;
                        // create onload functions for the LoadPanel and the native 
browser methods
                        // Liam - but they don't seem to do anything - are they 
overridden elsewhere?
                        lyr.onload = function(){};
                        lp.loadElement.onload = function(){};
                }
                else if (is.ns6) {
                        // ref the content of the LoadPanel
                        var html=e.body.innerHTML;
                        // set the innerHTML of the LoadPanel to the external content 
(clearing the native browser method)
                        lp.elm.innerHTML=html;
                        // explicitly delete the native browser method
                        lp.loadElement=null;
                }
                // after loading the external content, wait to do it all again with 
the next url in the Queue
                setTimeout('LoadQueue.continueLoad()',200);
        }
};
/*
        Contines loading the next url in the Queue after loading content into 
LoadPanels, or exiting early
*/
LoadQueue.continueLoad=function() {
        var q=LoadPanel.queue;
        // get the current url that has just been dealt with
        q.currentLoadPanel.loadHandler(q.currentURL);
        // enable the Queue to move on
        q.busy=false;
        // if there is another url in the Queue load it
        if (q.queue[q.index]) q.loadNext();
        // otherwise let the browser get on with things (from events.js)
        else DynAPI.document.captureMouseEvents();
};
/*
        Create the LoadQueue automatically on loading this file
*/
LoadPanel.queue=new LoadQueue();

Reply via email to