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