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