PDA

View Full Version : >1 iFrame: Dont want to re-reload all scripts!


Choleriker
12-15-2006, 10:56 PM
All updates you find in the flow of that post.

Update: 2007-02-02, just more simpler and working way
Update: 2006-12-17, final version with test-page see a few posts below
Update: 2006-12-18:
1. to minimize the traffic completely i added the possibility to preload stylesheets with the LoadHandler :lol:
2. fixed issue while using the ScriptHandler as singleton in an new opened window

Hello,

I use many iframes embedded in an BorderLayout that are using yui-ext byitself. Using iframes give you more simplicity on destructing containing objects and you will not have any (huge or small) memory leaks while long running applications which has many objects to handle. On my local developer machine all works fine and there are no performance problems, but after uploading it, i found out that every iframe loads to long. The problem is that i have disabled the explorer cache like all web developers are doing (Im using jonnychache in firefox). Thatz not nice. I tried to get a copy of the YAHOO-object in a iframe:


YAHOO = window.top.window.YAHOO;


But it doesnt work because there are several object-extensions and prototype addons and there are methods that uses 'document' directly. Everytime i tried to use maybe getEl() i get only DOM objects in the frame which has loaded the yui extensions.

Here is my solution. With this you application loads all scripts only ones, and you can inject the code in all iframes you want without reloading all scripts:

At the top level i have a nearly blank page which only contains a load-indicator and one script-include. The script containing in that include you can even write directly into that page, but i do it this way, because of design / imlementation separation:

index.htm

<html>
<head>
<title>Loader-Sample</title>
<script></script>
</head>
<body>
<div>Ladevorgang, bitte warten...</div>
</body>
</html>


init.js

window.LM = function() {
return {
load: function(scripts) {
this.scripts = scripts;
setTimeout(scripts+';LM.init();', 0);
},

documentReady: function() {
window.onload = null;
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
iframe.style.display = 'none';
iframe.src = "script_loader.htm";
}
};
}();
window.onload = window.LM.documentReady;


As you can see, this one does not anything else than append an iframe to document (invisible with display:none) and load then a page which is named script_loader.htm! The load method will be called if we have get all content of the scripts we need at this app. See the second line in function load(), with it, i will execute the scripts contents i get as a string after loading global at this (which is window). But before i save this script-code in the LM object. This give us the possibility to trigger scripts code (the object and extensions of yui, yui-ext and our application) at every frame simply by executing it at the target frame, iframe, opened windows or where we need it. You will wonder why im adding to that call a method LM.init() which isnt defined yet. I will later explain that fact.

script_loader.htm

<html>
<head>
<title>Loader-Home</title>
<script></script>
<script></script>
<script></script>
<script>

var _scripts = [
"Utils/yui_0.12.0/build/yahoo/yahoo.js",
"Utils/yui_0.12.0/build/dom/dom.js",
"Utils/yui_0.12.0/build/event/event.js",
"Utils/yui_0.12.0/build/logger/logger.js",
"Utils/yui_0.12.0/build/dragdrop/dragdrop.js",
"Utils/yui_0.12.0/build/animation/animation.js",
"Utils/yui_0.12.0/build/connection/connection.js",
"Utils/yui_0.12.0/build/container/container.js",
"Utils/yui_0.12.0/build/treeview/treeview.js",
"Utils/yui-ext_0.33-rc3/yui-ext-debug.js",
"ScriptLoader.js",
"Utils/tecbehind_1.0/source/Common.js",
"Utils/tecbehind_1.0/source/Date.js",
"Utils/livemonitor_1.3/Texts.aspx",
"Utils/livemonitor_1.3/source/Application.js"
];

window.onload = function() {
(new ScriptLoader()).startRequest(_scripts, function(scriptStr) {
window.top.window.LM.load(scriptStr);
});
}
</script>
</head>
<body>
Hey, dont mess up with my code :)
</body>
</html>


This page loads now the yahoo-script and the yahoo-connection script to get the common connection things ready. The next important thing + javascript include is a a script which contains one single, its the next one i will describe. Then we have a simple ja-script block which defines all scripts we are need, exactly in this order, they have to be load! And on the window.onload-event we simply create a new ScriptLoader object and call its method startRequest. The first parameter is our script-array, and the second one is a callback which is called after all scripts are loaded. As you can see the callback will get a string. This string contains the content of the loaded script in the correct order. That callback simply calls the load method of our LM object (which lives at the top-window!) from init.js. Here is the code for the ScriptLoader:

ScriptLoader.js

ScriptLoader = function() {
this.scripts = new Array();
}
ScriptLoader.prototype = {
handleSuccess:function(o){
// Connection-Object mit der entsprechenden Transaction-ID finden und dies danach mit dem Script-Text ersetzen
var _tl = this.transactions.length;
for(var i=0; i<_tl; i++) {
if(this.transactions[i].tId==o.tId) {
this.results[i]=o.responseText;
this.loadedScripts++;
if(this.loadedScripts==this.scripts.length && this.connectionsTriggered) {
this.connectionsTriggered = false;
this.callback(this.results.join(''));
delete this.results; delete this.loadedScripts; delete this.transactions;
}
break;
}
}
},
handleFailure:function(o){
throw "Fehler beim Lesen des Scriptes '"+this._scripts[this.currentScript]+"': "+o.resonseText;
},
startRequest:function(scr, fn) {
if(typeof fn!='undefined') this.callback = fn;
if(typeof scr!='undefined') {
this.transactions = [];
this.results = [];
this.loadedScripts = 0;
this.scripts = scr;
this.connectionsTriggered = false;
var callback = {
success:this.handleSuccess,
failure:this.handleFailure,
scope: this
}
for(var i=0; i<scr>0)
document.body.removeChild(document.body.firstChild);
alert('OK done! Bring up your layout.');
}
});

// Das globale LM Object um das Anwendungs-Objekt erweitern
APP = new YAHOO.livemonitor.Application();
Object.extend(LM, APP);


This one is the last script of the loaded scripts and extends our LM object. This LM object i wanted to be the global application handler. But my global application handler has to be i sub-class of YAHOO.ext.util.Observable. But this is only possible after yui-ext is loaded. This way fixes another issue in IE: if you try to define the load method directly in the object LM, the YAHOO object isnt available because of scope. Yes, ok, you can give the YAHOO object as a parameter, but this is no nice way. The last thing which is one of the importants: i dont want to make my code easy readable. The most common problem of ajax is the security. How easier you code is readable, it will be more easier to understand, that is not i want at all because my application handles any sensitive user data. (but it is very well secured).

Now we have it. If you now load an iframe maybe as a ContentPanel in BorderLayout, you have no more to do to as calling one simply method in your iframe page to get the complete framework run:


function loadScripts() {
setTimeout(window.top.window.LM.scripts, 0);
}


With this way nested iframes in any depth are no problem!

Addendum:

This is the code of my developer machine. While going live, the ScriptLoader only loads one (huge) script, which is compressed. Use jacks very nice JSBuilder for that! But after compression you will see that the loading time is too always to much for loading it in every iframe.

One last thing is important, its Caching on your developer machine: I have tried this scripts in IE, but with loaded "IE Developer Toolbar" with "Disable > Cache", you ie will cache the script loaded by iframe! Even press STRG+F5 isnt enough! At best, develop in Firefox with installed JonnyCache extension. In this way you will get no problems while developing! I dont use parameters at my script url's so i can ensure ie not to use cache maybe by changing connect-call to:


for(var i=0; i<scr.length; i++) this.transactions[i] = YAHOO.util.Connect.asyncRequest('GET', scr[i]+'?_p='+escape(new Date()), callback);


Thatz it!

Any suggestions / addons or so to it?

Choleriker
12-16-2006, 12:44 AM
How to show full script tags in code examples?

jack.slocum
12-16-2006, 01:32 PM
That's a nice write up, thank you for taking the time to explain it!

What would be really cool is a very simple page which uses it, this way I can fool around with it. It might be something that could be taken a little further and used like an "import" system.

Jack

(Checking "Disable HTML in this post" should keep it from stripping your code)

Edit: I just turned it off globally.

Choleriker
12-16-2006, 02:52 PM
After writing a huge answer to your post i get a message "Die spammer!" with IE7. Thatz not nice! Now you can only get the short version:

I have to find a solution for the following issues of the prototype at the top:

There are more then one scripts types, at last there are 4 types:

1. Static object extensions / object definitions (like YAHOO) that has to be loaded in every iframe
2. Application worker that ony has to be loaded system-wide
3. Configuration files that never change, like maybe animation-durations, the BorderLayout config etc.
4. Configurations that are dynamic, maybe the registry, which changes after a user logged (the siteMap changes at this point) or Texts that can be changed by the user switching the language

At last i get common scope issues in different browsers. I want to get in run in all, and get it simplier, not splitted in such many script files only for the loading functions.

That prototype at the top isnt after that things not only more that an prototype.

jack.slocum
12-16-2006, 04:39 PM
Sorry about that, you must have been writing while I deployed the anti-spam update.

Choleriker
12-17-2006, 05:04 AM
Sorry about that, you must have been writing while I deployed the anti-spam update.
Lol yeah after the post with the real porn link :) i have read it.

Choleriker
12-17-2006, 02:00 PM
After getting all issues of the top version and adding a little bit to it, here is the final version. This version hasnt a ScriptLoader, its mutated to ScriptHandler! All you have to do to get the code run, is to update the path's of the yahoo-includes and the include definition in index.htm to provide the scripts you want to load.

You will ask why it is important to load the ScriptHandler in an iframe, the code can work by using it directly in an page: yes, thatz right. But while we use yui to get the scripts, you fill see that YAHOO-object isnt really load correctly at IE, so do it in an iframe.

I have tried it on the following browser:
- IE 6
- IE 7
- Firefix 1.5
- Firefox 2
- Opera 9.02
- Netscape 8.1.2
I dont have a mac or a linux installation at this time, so please let me know if the code works on Safari / other browsers.

Dunno if all of the code is clear, if not, let me know. I have tried to comment the source code where informations needed. Please provide your suggestions if you get ideas to it!

ScriptHandler.js

if(typeof ScriptHandler=='undefined') {
/** Helpers **/
Object.extend = function(destination, source, keepExistingTargets) {if(typeof keepExistingTargets=='undefined') keepExistingTargets = false;for (var property in source) {if(!keepExistingTargets)destination[property] = source[property];else if(typeof destination[property]=='undefined') {destination[property] = source[property];}}return destination;};Object.extend(String.prototype, {toQueryParams: function() {var pairs = this.split('?');if(pairs.length==1) return [];pairs = pairs[1].toString().split('&');var _l = pairs.length;var _back = {};for(var i=0; i<_l; i++) {var _splittedParams = pairs[i].split("&");var _lj = _splittedParams.length; for(var j=0; j<_lj; j++) {var _splitted = _splittedParams[j].split("=");_back[_splitted[0]] = _splitted[1];}}return _back;}});

/**
* IE sniff that let us know if the browser is IE.
**/
var MSIE/*@cc_on =1@*/;

/** A counter which is used to specify inject's **/
var _currentInjectId = 0;
/** This array contains the current injections. We need it to use callbacks after a script was run **/
var _injectTransactions = new Array();
/**
* Injects the code of the param script in the scope of the targetWindow.
* @param {window} targetWindow The scope, where the script has to be laoded.
* @param {String, text/javascript} script The content of the scripts which has to execute.
* @param {Object optional} Additional options to that method, which can have the following properties:

* <ul>
* forceReturn:{Boolean optional, standard=false} If you need a return from the code, set this to true.
* [/list]
* @param {Boolean optional, standard=false} forceReturn If you need a return from the code, set this to true.
**/
inject = function(targetWindow, script, options) {
var _options = {forceReturn:true};
if(typeof options!='undefined') Object.extend(_options, options);
// IE has a nice function (http://msdn2.microsoft.com/en-us/library/ms536420.aspx) for us.
// But i think (not sure) this exists only in windows-version of IE
// A problem of that execScript is that isnt returns anything. So, if you need a return, we cant use it.
if(!_options.forceReturn && targetWindow.execScript)
targetWindow.execScript(script);
else if(MSIE) return targetWindow.eval(script);
else if(eval.call) return eval.call(targetWindow, script);
else throw "Cant find any method which can inject you code. Please email your browser-name and version to mb@tecbehind.de. Thank you!";
};

// to get it run we will need the a yui-ext function extension, that maybe havnt loaded until now
Function.prototype.createCallback = function(){

var args = arguments;
var method = this;
return function() {
return method.apply(window, args);
};
};

/** Define the script types which are usable **/
// scripts that are needed on every iframe, including static configurations
var ST_COMMON = 0;
// script where only needed ones, global app workers etc.
var ST_APP = 1;
// scripts that contains informations that do not have the same value at the lifecycle of the application
var ST_REGISTRY = 2;

/**
* The ScriptHandler which handles loading and instantiating of scripts.
* At most time this object is used as a singleton. At last, the script handler you instantiate at first in your app-lifecycle will be
* a singleton, which is accessible at this top of you window/frame tree with the shorthand $SH. After including that script at a frame, iframe or a window
* opened from that page, you will have that ScriptHandler accessible over $SH at you window too.
* you will have an object named $SL at self. If you dont want this behavior, simply define a variable names SH_NO_SINGLETON=true before including that script!
*
* @class ScriptHandler
* @constructor
* @param {Array} scripts The scripts which it has to handle.
**/
ScriptHandler = function(scripts) {
this.scripts = scripts;
}
ScriptHandler.prototype = {
handleSuccess:function(o){
// Connection-Object mit der entsprechenden Transaction-ID finden und dies danach mit dem Script-Text ersetzen
var _l = this.scripts.length;
for(var i=0; i<_l; i++) {
var _s = this.scripts[i];
if(typeof _s.transaction!='undefined' && _s.transaction.tId==o.tId) {
_s.content=o.responseText;
delete this.scripts[i].transaction;
// if not all scripts loaded completely, loaded will be false, so we know if that callback
// is while initialization / or by reloading a registry-script
if(!this.loaded) {
this.loadedScripts++;
if(this.loadedScripts==this.scripts.length && this.connectionsTriggered) {
this.connectionsTriggered = false;
this.loaded = true;
// is this our main-SH?
if((typeof SH_NO_SINGLETON!='boolean' || !SH_NO_SINGLETON) && typeof window.$SH=='undefined') {
if(typeof console!='undefined') console.log('ScriptHandler() main-instance not defined, letz use this one');
// to define that object first with var forces the browser to make a (there)local member. Dunno if its needed, but
// maybe in older browser it is. This at last is that, what all javascript-sites which you can google say you about scope.
// At last this hasnt work for me to getting a local copy on a window, excepts for IE
// NOTE: we force to get a return from the injected code because of not running it asynchronously.
inject(window.top.window, "var $SH = null;", true);
window.top.window.$SH = this;
inject(window.top.window, "$SH.inject(self)", true);
// maybe our main-SH was loaded at an external window, so letz try to inject this one to the opener too, but
// without handling any error while doing this
if(window.opener)
try {
inject(opener.window.top.window, "var $SH = null;", true);
opener.window.top.window.$SH = this;
inject(opener.window.top.window, "$SH.inject(self)", true);
}catch(e) {
}
}
if(typeof this.fn=='function') this.fn(this);
}
}else{
if(typeof _s.callback=='function') {
_s.callback(o.responseText);
delete this.scripts[i].callback;
}
}
break;
}
}
},
/**
* Returns the content of the scripts in that case that they are loaded. If they arent loaded at this time, this method will throw a exception.
* @param {Array, Optional} types The type of the scripts we want. If that parameter is empty all scripts will be returned.
**/
getScripts: function(types) {
if(!this.loaded) throw "You cant call getScripts() before they arent completely loaded. Use init() to do that";
// we dont want to iterate in every loop all types, so make that now.
var _common = false, _app = false, _registry = false;
if(typeof types=='undefined') {
_common=true; _app=true; _registry=true;
}else if(typeof types!='undefined') {
for(var i=0; i<types.length; i++)
switch(types[i]) {
case ST_COMMON: _common=true; break;
case ST_APP: _app=true; break;
case ST_REGISTRY: _registry=true; break;
default: throw "Script-Type "+types[i]+" isnt a known script-type"; break;
};
}else throw "You cant call ScriptHandler.getScripts() with an argument of type "+typeof types;
// iterate over the scripts and get the content of that ones we need
var _a = new Array();
var _l = this.scripts.length;
for(var i=0; i<_l; i++) {
var _s = this.scripts[i];
if(typeof _s.type=='undefined' && _common) _a.push(_s.content); else {
switch(_s.type) {
case ST_COMMON: if(_common) _a.push(_s.content); break;
case ST_APP: if(_app) _a.push(_s.content); break;
case ST_REGISTRY: if(_registry) _a.push(_s.content); break;
};
}
}
return _a.join(';');
},
/**
* Load the scripts with the given types into the scope of the targetWindow.
* @param {window} targetWindow The scope, where the scripts has to be loaded.
* @param {String optional} A string contains a javascript which has to add to the injected script at the end. This gives you the possibility to implement (for example) a callback.
* @param {Array, Optional} types The type of the scripts we want. If that parameter is empty all scripts will be returned.
**/
inject: function(targetWindow, addToScripts, types) {
inject(targetWindow, this.getScripts(types)+(typeof addToScripts=='string' ? ";"+addToScripts : ";"));
},
/**
* Reloads the script with the given id. Only scripts of type ST_REGISTRY can be reloaded. If you try that on script with another type, an exception
* will be thrown. If the script isnt defined, an exception will be thrown too.
* @param {String} scriptId The id of the script, which has to be reloaded.
* @param {Function optional} The callback function which has to be called after loading.
**/
reloadScript: function(scriptId, fn) {
var _l = this.scripts.length;
for(var i=0; i<_l; i++) {
var _s = this.scripts[i];
if(_s.id==scriptId) {
if(typeof _s.type=='undefined' || _s.type!=ST_REGISTRY) throw "Only scripts of type ST_REGISTRY can be reloaded";
this.script[i].callback = fn;
var callback = {
success:this.handleSuccess,
failure:this.handleFailure,
scope: this
}
YAHOO.util.Connect.asyncRequest('GET', _s.url, callback);
return;
}
};
throw "The registry-script with the id "+scriptId+" was not found, so i cant reload it";
},
handleFailure:function(o){
throw "Error while loading the script '"+this.scripts[this.currentScript].url+"': "+o.resonseText;
},
/**
* Loads all scripts.
* @param {Function optional} fn Callback which has to be called after the scripts are loaded. This method gets an instance of that handler as the argument.
**/
init:function(fn) {
this.fn = fn;
this.loaded = false;
this.injectTo
this.loadedScripts=0;
var _self = this; // tricky :)
var callback = {
success:_self.handleSuccess,
failure:_self.handleFailure,
scope: _self
};
var _l = this.scripts.length;
for(var i=0; i<_l; i++) this.scripts[i].transaction = YAHOO.util.Connect.asyncRequest('GET', this.scripts[i].url, callback);
this.connectionsTriggered = true;
}
};

if(typeof SH_NO_SINGLETON!='boolean' || !SH_NO_SINGLETON) {
// letz check if there is an ScriptHandler which is our main-one
if(window.top.window!=window && window.top.window.$SH) {
if(typeof console!='undefined') console.log("ScriptHandler found at top-window");
window.$SH = window.top.window.$SH;
} else if(window.opener && window.opener.top.window.$SH && window.opener.top.window!=window) {
if(typeof console!='undefined') console.log("ScriptHandler found at the top-window of the opener");
window.$SH = window.opener.top.window.$SH;
};
}
}


loader.htm

<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title></title>
<script type="text/javascript" src="Utils/yui_0.12.0/build/yahoo/yahoo-debug.js" language="javascript"></script>
<script type="text/javascript" src="Utils/yui_0.12.0/build/connection/connection-debug.js" language="javascript"></script>
<script type="text/javascript" src="ScriptHandler.js"language="javascript"></script>
<script type="text/javascript" language="javascript">

window.onload = function() {
// get the script we have to load
var params = (new String(location.href)).toQueryParams();
if(typeof params["scriptDef"]=='undefined') {
alert('Please provide the scripts you want to load with the url paramer "scriptDef" which contains the name of the array which contains \'em');
return;
}
var _scripts = inject(window.parent.window, params["scriptDef"], true);
// get up the script-loader
var SH = new ScriptHandler(_scripts);
// then initialize 'em and test it in an iframe after that
SH.init(function() {
var params = (new String(location.href)).toQueryParams();
if(typeof params["callback"]!='undefined') {
inject(top.window, params["callback"]+"();");
}
});
}
</script>

</head>
<body>
</body>
</html>


index.htm: sample page which uses the loader

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Ladevorgang, bitte warten...</title>
<script type="text/javascript" src="Utils/yui_0.12.0/build/yahoo/yahoo-min.js" language="javascript"></script>
<script type="text/javascript" src="Utils/yui_0.12.0/build/connection/connection-min.js" language="javascript"></script>
<script type="text/javascript" src="ScriptHandler.js"language="javascript"></script>
<script type="text/javascript" language="javascript">
var scriptsToLoad = [
// global Scripts of type ST_COMMON dont need to set the type property, because ST_COMMON is the standard-type
// and they dont need a id because they dont have to be reloaded
{url:"Utils/yui_0.12.0/build/yahoo/yahoo.js"},
{url:"Utils/yui_0.12.0/build/dom/dom.js"},
{url:"Utils/yui_0.12.0/build/event/event.js"},
{url:"Utils/yui_0.12.0/build/logger/logger.js"},
{url:"Utils/yui_0.12.0/build/dragdrop/dragdrop.js"},
{url:"Utils/yui_0.12.0/build/animation/animation.js"},
{url:"Utils/yui_0.12.0/build/connection/connection.js"},
{url:"Utils/yui_0.12.0/build/container/container.js"},
{url:"Utils/yui_0.12.0/build/treeview/treeview.js"},
{url:"Utils/yui-ext_0.33-rc3/yui-ext-debug.js"},
{url:"Utils/tecbehind_1.0/source/Common.js"},
{url:"Utils/tecbehind_1.0/source/Date.js"},
{url:"Utils/tecbehind_1.0/source/Tools.js"},
// maybe the user wants to change the language, so the type is ST_REGISTRY
{id:"Texts", type:ST_REGISTRY, url:"Utils/livemonitor_1.3/Texts.aspx"},
{id:"Registry", type:ST_REGISTRY, url:"Utils/livemonitor_1.3/Registry.aspx"},
{url:"Utils/livemonitor_1.3/Configuration.js"},
{url:"Utils/livemonitor_1.3/source/yui-ext.js"}
];

// to see if our scope works well we simply define a var scopeName
var sName = 'index.htm';

function showCurrentScope() {
alert(sName);
}

// renders tests to this window to try if our ScriptHandler works well
renderTests = function() {
var _wrapper = document.getElementById('iframeWrapper');
var _frame = document.createElement('iframe');
_wrapper.appendChild(_frame);
_frame.id = "testIframe1";
// This is the first test byitself. Is all remote scripts are loaded well we can use the EventManager of the yui-ext.
// Look that i dont override the scope of the event-handler, because
// if all works fine the scope has to come from inject and we try not to get the showCurrentScope
// method by taking top.window, no we target it with parent! And we check the scope where this script evals
// by checking simply the window object.
YAHOO.ext.EventManager.on(_frame, 'load', function() {
inject(window.top.window.frames[0], "if(window.top.window==window) alert('Scope isnt correct, what we want is to have it in iframe!'); else "+showCurrentScope.toString());
});
_frame.frameborder = 'no';
_frame.style.visibility = 'hidden';
_frame.style.width = '90%';
_frame.src = 'test1.htm';

getEl("loadLayer").hide(true, .5);
setTimeout("getEl('testIframe1').show(true, .5);", .5);

document.title = "ScriptHandler-Example";
status = "All scripts loaded well, try the examples now! If you find any issues, let me know: mb@tecbehind.de";
return;
};
</script>

</head>
<body>
<div id="loadLayer" style="color: #3300cc; font-family: Tahoma, Arial;">
Loading scripts and tests, wait a moment...
</div>
<center id="iframeWrapper">
</center>
<iframe src="loader.htm?callback=renderTests&scriptDef=scriptsToLoad" style="display:none;position:absolute;"></iframe>
</body>
</html>


test1.htm: another page taking advantage of the ScriptHandler to show how it works and it can be used which is loaded by the the test-page above.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>test1</title>

<script type="text/javascript" language="javascript">
// to see if our scope works well we simply define a var scopeName
var sName = 'test1.htm';
var shLoaded = false

function checkScriptHandler() {
if(typeof $SH=='undefined') {
alert('Cant find one, anything doesnt work!');
return;
}
if(!shLoaded) {
shLoaded = true;
alert('SH wasnt injected, will do that now...');
$SH.inject(this, "callAfterSHInject()");
} else
alert(
"Count of scripts: "+$SH.scripts.length+"\r\n"+
"Charachters currently in all scripts:"+$SH.getScripts().length+"\r\n"+
"Characters in common-scripts: "+$SH.getScripts([ST_COMMON]).length+"\r\n"+
"Characters in app-scripts: "+$SH.getScripts([ST_APP]).length+"\r\n"+
"Characters in registry-scripts: "+$SH.getScripts([ST_REGISTRY]).length
);
}
function checkInject() {
inject(top.window, "alert('Whats we need is index.htm: '+sName);");
}

function callAfterSHInject() {
alert('Injection ok, look the button you have clicked!');
getEl('shButton').hide(true, .5);
setTimeout('var _el=getEl("shButton"); _el.setStyle("background-color", "transparent"); _el.dom.innerHTML="Click me again to get Informations about $SH!"; _el.show(true, .5);', 1000);
}
</script>

<script type="text/javascript" src="ScriptHandler.js"
language="javascript"></script>

</head>
<body>
<button onclick="checkScriptHandler()" id="shButton">
Click to find out anything about our ScriptHandler!</button>
</body>
</html>

jack.slocum
12-17-2006, 03:21 PM
I will be playing with this later, thanks for sharing!

Choleriker
12-18-2006, 06:11 AM
Here is a update of the code.

The added possibility to preload stylesheets has to be activates explicitly because i dont wanted to break your design while using it. At any page where you want to have to auto-render your preloaded styles automatic (for sure by using the SH as singleton) you have to set a constant, which targets the system to make so, before including ScriptHandler.js:


<script type="text/javascript">
SH_NO_AUTOINJECT_STYLES = false;
</script>


Or you can inject the preloaded styles by calling:


<script type="text/javascript">
$SH.injectStyles();
</script>


at the window where you need it too.

For that we have a new 'script'-type ST_STYLE. The most common change is the new method injectStyles(). For simplicity i insered the complete code below.


if(typeof ScriptHandler=='undefined') {
/** Helpers **/
Object.extend = function(destination, source, keepExistingTargets) {if(typeof keepExistingTargets=='undefined') keepExistingTargets = false;for (var property in source) {if(!keepExistingTargets)destination[property] = source[property];else if(typeof destination[property]=='undefined') {destination[property] = source[property];}}return destination;};Object.extend(String.prototype, {toQueryParams: function() {var pairs = this.split('?');if(pairs.length==1) return [];pairs = pairs[1].toString().split('&');var _l = pairs.length;var _back = {};for(var i=0; i<_l; i++) {var _splittedParams = pairs[i].split("&");var _lj = _splittedParams.length; for(var j=0; j<_lj; j++) {var _splitted = _splittedParams[j].split("=");_back[_splitted[0]] = _splitted[1];}}return _back;}});

/**
* IE sniff that let us know if the browser is IE.
**/
var MSIE/*@cc_on =1@*/;
;
/**
* Injects the code of the param script in the scope of the targetWindow.
* @param {window} targetWindow The scope, where the script has to be laoded.
* @param {String, text/javascript} script The content of the scripts which has to execute.
* @param {Object optional} Additional options to that method, which can have the following properties:

* <ul>
* forceReturn:{Boolean optional, standard=false} If you need a return from the code, set this to true.
* [/list]
* @param {Boolean optional, standard=false} forceReturn If you need a return from the code, set this to true.
**/
inject = function(targetWindow, script, options) {
var _options = {forceReturn:true};
if(typeof options!='undefined') Object.extend(_options, options);
// IE has a nice function (http://msdn2.microsoft.com/en-us/library/ms536420.aspx) for us.
// But i think (not sure) this exists only in windows-version of IE
// A problem of that execScript is that isnt returns anything. So, if you need a return, we cant use it.
if(!_options.forceReturn && targetWindow.execScript)
targetWindow.execScript(script);
else if(MSIE) return targetWindow.eval(script);
else if(eval.call) return eval.call(targetWindow, script);
else throw "Cant find any method which can inject you code. Please email your browser-name and version to mb@tecbehind.de. Thank you!";
};

// to get it run we will need the a yui-ext function extension, that maybe havnt loaded until now
Function.prototype.createCallback = function(){

var args = arguments;
var method = this;
return function() {
return method.apply(window, args);
};
};

/** Define the script types which are usable **/
// scripts that are needed on every iframe, including static configurations
var ST_COMMON = 0;
// script where only needed ones, global app workers etc.
var ST_APP = 1;
// scripts that contains informations that do not have the same value at the lifecycle of the application
var ST_REGISTRY = 2;
// Stylesheets
var ST_STYLE = 3;

var ST_DEBUG = true;

/**
* The ScriptHandler which handles loading and instantiating of scripts.
* At most time this object is used as a singleton. At last, the script handler you instantiate at first in your app-lifecycle will be
* a singleton, which is accessible at this top of you window/frame tree with the shorthand $SH. After including that script at a frame, iframe or a window
* opened from that page, you will have that ScriptHandler accessible over $SH at you window too.
* you will have an object named $SL at self. If you dont want this behavior, simply define a variable names SH_NO_SINGLETON=true before including that script!
*
* @class ScriptHandler
* @constructor
* @param {Array} scripts The scripts which it has to handle.
**/
ScriptHandler = function(scripts) {
this.scripts = scripts;
}
ScriptHandler.prototype = {
handleSuccess:function(o){
// Connection-Object mit der entsprechenden Transaction-ID finden und dies danach mit dem Script-Text ersetzen
var _l = this.scripts.length;
for(var i=0; i<_l; i++) {
var _s = this.scripts[i];
if(typeof _s.transaction!='undefined' && _s.transaction.tId==o.tId) {
_s.content=o.responseText;
delete this.scripts[i].transaction;
// if not all scripts loaded completely, loaded will be false, so we know if that callback
// is while initialization / or by reloading a registry-script
if(!this.loaded) {
this.loadedScripts++;
if(this.loadedScripts==this.scripts.length && this.connectionsTriggered) {
this.connectionsTriggered = false;
this.loaded = true;
// is this our main-SH?
if((typeof SH_NO_SINGLETON!='boolean' || !SH_NO_SINGLETON) && typeof window.$SH=='undefined') {
if(typeof console!='undefined') console.log('ScriptHandler() main-instance not defined, letz use this one');
// to define that object first with var forces the browser to make a (there)local member. Dunno if its needed, but
// maybe in older browser it is. This at last is that, what all javascript-sites which you can google say you about scope.
// At last this hasnt work for me to getting a local copy on a window, excepts for IE
// NOTE: we force to get a return from the injected code because of not running it asynchronously.
inject(window.top.window, "var $SH = null;", true);
window.top.window.$SH = this;
inject(window.top.window, "$SH.inject(self)", true);
// maybe our main-SH was loaded at an external window, so letz try to inject this one to the opener too, but
// without handling any error while doing this
if(window.opener && typeof window.opener!='undefined' && !window.opener.top.window==window.top.window)
try {
inject(opener.window.top.window, "var $SH = null;", true);
opener.window.top.window.$SH = this;
inject(opener.window.top.window, "$SH.inject(self)", true);
}catch(e) {
}
// inject the styles if needed
inject(window.parent.window, "function _dontInjectFiles(){return typeof SH_NO_AUTOINJECT_STYLES=='undefined' ? false : SH_NO_AUTOINJECT_STYLES;}", true);
var _dontInjectStyles = window.parent.window._dontInjectFiles();
if(!_dontInjectStyles) this.injectStyles(window.top.window);
}
if(typeof this.fn=='function') this.fn(this);
}
}else{
if(typeof _s.callback=='function') {
_s.callback(o.responseText);
delete this.scripts[i].callback;
}
}
break;
}
}
},
/**
* Returns the content of the scripts in that case that they are loaded. If they arent loaded at this time, this method will throw a exception.
* @param {Array, Optional} types The type of the scripts we want. If that parameter is empty all scripts will be returned, excepts of stylesheets
**/
getScripts: function(types) {
var _debug = function(_a, _s) {
if(ST_DEBUG) {
_a.push("/**\r\n\tSCRIPT: "+_s.url+"\r\n**/");
if(typeof console!='undefined') _a.push("console.log('Running SCRIPT: "+_s.url+", lines: "+(_s.content.split("\n").length)+"');");
};
};
if(!this.loaded) throw "You cant call getScripts() before they arent completely loaded. Use init() to do that";
// we dont want to iterate in every loop all types, so make that now.
var _common = false, _app = false, _registry = false; _styles = false;
if(typeof types=='undefined') {
_common=true; _app=false; _registry=true;
}else if(typeof types!='undefined') {
for(var i=0; i<types.length; i++)
switch(types[i]) {
case ST_COMMON: _common=true; break;
case ST_APP: _app=true; break;
case ST_REGISTRY: _registry=true; break;
case ST_STYLE: _styles=true; break;
default: throw "Script-Type "+types[i]+" isnt a known script-type"; break;
};
if(_styles && (_registry || _app || _common)) throw "It makes no sence to mix stylesheets with javascript, types: "+(types.join(","));
}else throw "You cant call ScriptLoader.getScripts() with an argument of type "+typeof types;
// iterate over the scripts and get the content of that ones we need
var _a = new Array();
var _l = this.scripts.length;
for(var i=0; i<_l; i++) {
var _s = this.scripts[i];
if((typeof _s.type=='undefined' && _common) || _styles) {
if(ST_DEBUG) {
_a.push("/**\r\n\tSCRIPT: "+_s.url+"\r\n**/");
if(typeof console!='undefined' && !_styles)
_a.push("console.log('Running SCRIPT: "+_s.url+"');");
};
_a.push(_s.content);
} else {
switch(_s.type) {
case ST_COMMON: if(_common) { _debug(_a, _s); _a.push(_s.content); }; break;
case ST_APP: if(_app) {_debug(_a, _s); _a.push(_s.content); }; break;
case ST_REGISTRY: if(_registry) { _debug(_a, _s); _a.push(_s.content); }; break;
}
}
}
return _a.join(';');
},
/**
* Load the scripts with the given types into the scope of the targetWindow.
* @param {window} targetWindow The scope, where the scripts has to be loaded.
* @param {String optional} A string contains a javascript which has to add to the injected script at the end. This gives you the possibility to implement (for example) a callback.
* @param {Array, Optional} types The type of the scripts we want. If that parameter is empty all scripts will be returned.
**/
inject: function(targetWindow, addToScripts, types) {
inject(targetWindow, this.getScripts(types)+(typeof addToScripts=='string' ? ";"+addToScripts : ";"));
},
/**
* Inject the stylesheets into the document. The document has to be loaded completely before you can use this!
**/
injectStyles: function(targetWindow) {
if(typeof targetWindow!='undefined') {
var _h = document.getElementsByTagName("HEAD")[0];
if(typeof _h!='undefined') {
if(MSIE) {
_h.appendChild(document.createTextNode('<style type="text/css">'+this.getScripts([ST_STYLE])+'</style>'));
}else{
var _styles = document.createElement("style");
_h.appendChild(_styles);
_styles.type = "text/css";
_styles.innerHTML = this.getScripts([ST_STYLE]);
}
}else document.write('<style type="text/css">'+this.getScripts([ST_STYLE])+'</style>');
}else{
targetWindow.$SH.injectStyles();
}
},
/**
* Reloads the script with the given id. Only scripts of type ST_REGISTRY can be reloaded. If you try that on script with another type, an exception
* will be thrown. If the script isnt defined, an exception will be thrown too.
* @param {String} scriptId The id of the script, which has to be reloaded.
* @param {Function optional} The callback function which has to be called after loading.
**/
reloadScript: function(scriptId, fn) {
var _l = this.scripts.length;
for(var i=0; i<_l; i++) {
var _s = this.scripts[i];
if(_s.id==scriptId) {
if(typeof _s.type=='undefined' || _s.type!=ST_REGISTRY) throw "Only scripts of type ST_REGISTRY can be reloaded";
this.script[i].callback = fn;
var callback = {
success:this.handleSuccess,
failure:this.handleFailure,
scope: this
}
YAHOO.util.Connect.asyncRequest('GET', _s.url, callback);
return;
}
};
throw "The registry-script with the id "+scriptId+" was not found, so i cant reload it";
},
handleFailure:function(o){
throw "Error while loading the script '"+this.scripts[this.currentScript].url+"': "+o.resonseText;
},
/**
* Loads all scripts.
* @param {Function optional} fn Callback which has to be called after the scripts are loaded. This method gets an instance of that handler as the argument.
**/
init:function(fn) {
this.fn = fn;
this.loaded = false;
this.loadedScripts=0;
var _self = this; // tricky :)
var callback = {
success:_self.handleSuccess,
failure:_self.handleFailure,
scope: _self
};
for(var i=0; i<this.scripts.length; i++)
this.scripts[i].transaction = YAHOO.util.Connect.asyncRequest('GET', this.scripts[i].url, callback);
this.connectionsTriggered = true;
}
};

if(typeof SH_NO_SINGLETON!='boolean' || !SH_NO_SINGLETON) {
// letz check if there is an ScriptHandler which is our main-one
if(window.top.window!=window && window.top.window.$SH) {
if(ST_DEBUG && typeof console!='undefined') console.log("ScriptHandler found at top-window");
window.$SH = window.top.window.$SH;
if(typeof SH_NO_AUTOINJECT_STYLES=='boolean' && !SH_NO_AUTOINJECT_STYLES) window.top.window.$SH.injectStyles(window.top.window);
} else if(window.opener && window.opener.top.window.$SH && window.opener.top.window!=window) {
if(ST_DEBUG && typeof console!='undefined') console.log("ScriptHandler found at the top-window of the opener");
window.$SH = window.opener.top.window.$SH;
if(typeof SH_NO_AUTOINJECT_STYLES=='boolean' && !SH_NO_AUTOINJECT_STYLES) window.opener.top.window.$SH.injectStyles(window.top.window);
};
}
}

Choleriker
12-19-2006, 02:31 PM
Hey,

after to implement the whole stuff at top i get many many issues regards to scope (lol im a javascript beginner), paths for images in css and many many more. So let try the code! It makes no sence.

A friend of mine has requested that working, i need it too, so there will come a well working version soon. I have run now the system here on my machine, with the half of code and only in one script. So the next version of that i will post is more simplier and more working at this one short top of that post.

Im no beginner in developing, but a bloody n00b at js, so it was hard for me to understand that scope-stuff. At last im german (im sure, you know it after reading my posts), the most good informations are not in german. So please forgive me!

But i will not share my code before it runs 100%, enough shame with this one before :)

pomata
12-20-2006, 06:01 PM
This is interesting... doing same with lots of Iframes....

Anybody come up with a solution?

ta

P

Choleriker
12-20-2006, 10:44 PM
How im said, its allright working here, if i have moved my app and my implementation makes no problems anymore and im sure its working i will post it.

kalebwalton
01-03-2007, 07:15 AM
Any more word on this?

Choleriker
01-05-2007, 04:35 AM
Place all your pages you load in an iframe in a subfolder in the same depth if you load you scripts and css if you use relative paths or use absolute paths (important is that all includes use the same include path!) and your browser is forced to cache it!

Choleriker
02-02-2007, 09:49 AM
I wanted to get the upper thing working in all browser, that i have get. But my solution was very 'ty' and too much lines of code, i started to spent one more day in that, and i found a very simple solution to share all JS code and all CSS code!

The short description: i just added 2 html tags in the top document. the first is a hidden textarea which is named StyleProxy and contains all CSS code which is needed. The second one is a script tag of type text/javascript which is named named ScriptProxy. In all nested iframes i only get the content of that both tags and inject it in the new document. One thing you have to ensure is that the main-page is fully loaded before load an nested iframe. The second thing is that if you have plitted you CSS and JS in many files like me you have to find a way to nest all the content of that code into the both tags or you take more than one tag for your JS and CSS and change the loading script you will find below. If you want to see it in action on my test-platform go to http://lm.dev.questnet.de.

Here is a more detailed description.

If you are using asp.net like me i have added my asp.net 2.0 components too. I handle more than one file for css and js i simply have created a access file (you can use any other database too). The database has the following structure and sample content:

http://tecbehind.de/useruploads/choleriker/db_1.jpg
http://tecbehind.de/useruploads/choleriker/db_2.jpg

It simply stores location, type and at last the order in where the has to be added. The i have made a simple sql view:


SELECT scripts_locations.location+'/'+scripts.file AS ['script'], scripts.type
FROM scripts_locations INNER JOIN (scripts INNER JOIN scripts_order ON scripts.id = scripts_order.script) ON scripts_locations.id = scripts.location
ORDER BY scripts_order.position;


Now letz look the code for the StyleProxy, important is to add this to the body, not to the head tag of your html. In asp.net iam using simply a repeater which iterates over the content of the selected lines, reads out the file-contents and insert it into html. In php is the same simple thing.


<textarea id="StyleProxy" style="display: none; visibility: hidden;">
/************* CSS CODE HERE ***********/

/** here my asp.net components which handles many stylesheets **/

<asp:accessdatasource id="StyleDataSource" runat="server" datafile="~/App_Data/ASPNetDB.mdb"
selectcommand="SELECT ['script'] AS column1 FROM [get_scripts] WHERE ([type] = 'css')">
</asp:accessdatasource>

<asp:Repeater ID="Repeater1" runat="server" DataSourceID="StyleDataSource" EnableViewState="False">
<ItemTemplate>
<%# System.IO.File.ReadAllText(Server.MapPath(Request.ApplicationPath)+"\\"+Eval("column1").ToString()) %>
</ItemTemplate>
</asp:Repeater>
</textarea>
<script type="text/javascript">
document.write(['<style type="text/css">',document.getElementById('StyleProxy').innerHTML,'</style>'].join('\r'));
</script>


As you can see after the code was parsed i simple add a new style proxy to get the styles rid.

At next the Script-Proxy-Code:


<asp:accessdatasource id="ScriptDataSource" runat="server" datafile="~/App_Data/ASPNetDB.mdb"
selectcommand="SELECT ['script'] AS column1 FROM [get_scripts] WHERE ([type] = 'js')">
</asp:accessdatasource>
<script type="text/javascript" id="ScriptProxy">
<uc3:Texts ID="Texts1" runat="server" />
<asp:Repeater ID="Repeater2" runat="server" DataSourceID="ScriptDataSource" EnableViewState="False">
<ItemTemplate>
<%# System.IO.File.ReadAllText(Server.MapPath(Request.ApplicationPath)+"\\"+Eval("column1").ToString()) %>
</ItemTemplate>
</asp:Repeater>
</script>


It is mostly the same like the StyleProxy, only that im using 'js' as file-type from the db. Its no need to inject this code.

So for a simpler access to these content if have added a script tag after this all:


<script type="text/javascript">

Ext.onReady(function() {
top.window._SCRIPTS = getEl('ScriptProxy').dom.innerHTML.toString();
top.window._STYLES = getEl('StyleProxy').dom.innerHTML.toString();

// HERE START LOADING OF THE IFRAMES
}


At last iam using in all documents which are loading in an nested iframe this in the HEAD tag:


<script type="text/javascript">
document.write(['<style type="text/css">',top.window._STYLES,'</style>'].join('\r'));
window.eval.call(window, top.window._SCRIPTS);
</script>


Thatz all! I found it very successfully and it isnt buggy like lazy loading the style and script content i have described above.

Wolfgang
03-02-2007, 07:48 PM
Hello,

i walked through the thread but still have difficulties to understand your solution.

Can you give an example without the sql database, for just yui-ext scripts and styles?

Thanks

Wolfgang

Choleriker
03-04-2007, 10:34 AM
Can you give an example without the sql database, for just yui-ext scripts and styles?


Hey Wolfgang,

just replace the code inside the <asp:XXX> tags with you styles / scripts! The rest is completely the same. Just ignore the asp tags, they do nothing else than read out the scripts / styles for more than one file and put it in there!

genius551v
03-25-2007, 01:08 PM
Hi,

This sound very interesting, but is a little complicate to understand to "rokies", so can you put some one example please?

tnks

Choleriker
03-26-2007, 03:36 AM
This sound very interesting, but is a little complicate to understand to "rokies", so can you put some one example please?

<textarea id="StyleProxy" style="display: none; visibility: hidden;">
/************* CSS CODE HERE ***********/

body {
font-face: Arial;
}
</textarea>
<script type="text/javascript">
document.write(['<style type="text/css">',document.getElementById('StyleProxy').innerHTML,'</style>'].join('\r'));
</script>


As you can see after the code was parsed i simple add a new style proxy to get the styles rid.

At next the Script-Proxy-Code:


<script type="text/javascript" id="ScriptProxy">
// Ext Code here
alert('Hello, im Loaded!');
</script>

<script type="text/javascript">

Ext.onReady(function() {
top.window._SCRIPTS = getEl('ScriptProxy').dom.innerHTML.toString();
top.window._STYLES = getEl('StyleProxy').dom.innerHTML.toString();

// HERE START LOADING OF THE IFRAMES
}


At last iam using in all documents which are loading in an nested iframe this in the HEAD tag:


<script type="text/javascript">
document.write(['<style type="text/css">',top.window._STYLES,'</style>'].join('\r'));
window.eval.call(window, top.window._SCRIPTS);
</script>

cdomigan
04-02-2007, 11:37 PM
Hi, I'm implementing a system with tabbed content with each tab loaded into an iframe. However I'm having a lot of trouble following the solution for importing scripts proposed in this thread. Is anybody able to simply explain this solution and what I need to include? Seems the solution is split/replicated over a number of posts....

Cheers,

Chris

mdelmarter
04-14-2007, 10:04 AM
Hi Chris,

I have been toying with this concept and have added an explanation of it along with an example using Ext on my blog: http://matthew.delmarters.com/weblog/injecting-javascript-and-css-into-iframes/

I would like to add my thanks to Choleriker for bringing this technique to light for me.

I hope the explanations and examples are helpful - let me know...

Matthew

genius551v
04-17-2007, 09:45 AM
mdelmarter, hey man great job and explication, tnks for shared

genius551v

Choleriker
04-19-2007, 10:11 AM
I hope the explanations and examples are helpful - let me know...


Thank you, nice one!

manugoel2003
05-02-2007, 04:15 AM
Edit: I have used 0.40 in the examples below.

Hi Chris,

I was using a lot of Iframes and was facing this problem, but I found your solution a bit confusing. I have come up with a simpler solution which avoids Iframes altogether. I am able to execute the scripts in the child page. It is working quite nicely. This is the function that does the trick:


function loadPage(url, proxy, target){
getEl(proxy).load(url, "", function (oElement, bSuccess, oConn, target){
if (bSuccess)
{
var html = oElement.dom.innerHTML;
getEl(target).update(html, true, function (){
alert("Dynamic loading completed");
});
}
else
{
alert("Failed to load Page. Please check the URL or try again later.");
}
}.createDelegate(null, [target], true));
}


You just need to call this function with 3 arguments

url - The url which needs to be loaded
proxy - ID of a proxy div which is hidden by default and is used to download the page content first which are then executed
target - ID of the target div in which the content will be finally rendered



Here is a sample implementation

PAGE1.htm (Note the loadPage function)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Compose</title>

<link rel="stylesheet" type="text/css" href="../library/yui-ext-40/resources/css/reset-min.css" />
<link rel="stylesheet" type="text/css" href="../library/yui-ext-40/resources/css/yui-ext-40.css" />
<link rel="stylesheet" type="text/css" href="../library/yui-ext-40/resources/css/ytheme-gray.css" />

<style type="text/css">
body {
margin: 10px;
}
#dynPageContainer {
border: solid 1px #999;
height: 200px;
margin: 10px 0px;
overflow: auto;
}
#proxy {
display: none;
visibility: hidden;
}
</style>

<script type="text/javascript" language="JavaScript" src="../library/yui/build/utilities/utilities.js"></script>
<script type="text/javascript" language="JavaScript" src="../library/yui-ext-40/yui-ext-40.js"></script>

<script type="text/javascript" language="JavaScript">
function loadPage(url, proxy, target){
getEl(proxy).load(url, "", function (oElement, bSuccess, oConn, target){
if (bSuccess)
{
var html = oElement.dom.innerHTML;
getEl(target).update(html, true, function (){
alert("Dynamic loading completed");
});
}
else
{
alert("Failed to load Page. Please check the URL or try again later.");
}
}.createDelegate(null, [target], true));
}

function init(){
new YAHOO.ext.Button("btnLoadPage", {
text: "Click here to load a dynamic page",
handler: loadPage.createCallback("page2.htm", "proxy", "dynPageContainer"),
minWidth: 50
});
new YAHOO.ext.Button("btnLoadBorderLayout", {
text: "Click here to load a Border Layout",
handler: loadPage.createCallback("page3.htm", "proxy", "dynPageContainer"),
minWidth: 50
});
}

YAHOO.ext.EventManager.onDocumentReady(init, this, true);
</script>
</head>
<body>

<div id="btnLoadPage"></div>
<div id="btnLoadBorderLayout"></div>
<div id="dynPageContainer"></div>
<div id="proxy"></div>

</body>
</html>


Here is the child page

PAGE2.htm (Note it does not uses any html or body tags)

<style type="text/css">
.childPage {
background: #f5f5f5;
font: 13px verdana;
height: 100%;
}
.childPage h1 {
margin: 0px;
padding: 5px;
font: 22px verdana;
background: #e5e5e5;
border-bottom: solid 1px #999;
}
.childPage p {
margin: 10px 5px 0px;
color: darkgreen;
}
</style>

<script type="text/javascript" language="JavaScript">
function B(){
alert("Hi");
}
YAHOO.ext.EventManager.onDocumentReady(function(){
new YAHOO.ext.Button("myButton", {
text: "Click Here",
handler: B,
minWidth: 50
});

}, this, true);
</script>


<div class="childPage">
<h1>This is the Page 2</h1>
<p>
This page was loaded using AJAX. If you can see the YUI-ext Button below then the javascript loaded in the parent page is accessible in this page as well.
</p>
<p>
<div id="myButton" style="margin-left: 5px;"></div>
</p>
</div>



However I am having trouble building a borderLayout from the child page as when I mention the "document.body" in "new YAHOO.ext.BorderLayout" in the child page, the border layout is rendered in the entire parent page and not in the div. That is quite expected, but I am not able to come up with a solution.

Any ideas anyone.

davidelias
05-02-2007, 05:10 AM
However I am having trouble building a borderLayout from the child page as when I mention the "document.body" in "new YAHOO.ext.BorderLayout" in the child page, the border layout is rendered in the entire parent page and not in the div. That is quite expected, but I am not able to come up with a solution.
Any ideas anyone.

Probably you need to render the new BorderLayout to "childPage" div.

manugoel2003
05-02-2007, 05:19 AM
I tried this


<style type="text/css">
.childPage {
background: #f5f5f5;
font: 13px verdana;
height: 100%;
}
.childPage h1 {
margin: 0px;
padding: 5px;
font: 22px verdana;
background: #e5e5e5;
border-bottom: solid 1px #999;
}
.childPage p {
margin: 10px 5px 0px;
color: darkgreen;
}
</style>

<script type="text/javascript" language="JavaScript">
sample = function(){
var layout;

return {
init : function(){
layout = new YAHOO.ext.BorderLayout("container", {
hideOnLayout: true,
west: {
title: "West Region",
initialSize: 100,
split: true,
titlebar: true,
collapsible: true
},
center: {
title: "Center Region",
split: true,
titlebar: true
}
});
layout.beginUpdate();
layout.add('west', new YAHOO.ext.ContentPanel('left'));
layout.add('center', new YAHOO.ext.ContentPanel('center'));
layout.endUpdate();

new YAHOO.ext.Button("myButton", {
text: "Click Here",
handler: this.handleClick,
minWidth: 50
});
},

handleClick : function(){
alert("Hi");
}
}
}();
YAHOO.ext.EventManager.onDocumentReady(sample.init, sample, true);
</script>


<div id="container" class="ylayout-inactive-content">
<div id="left" class="ylayout-inactive-content"></div>
<div id="center" class="ylayout-inactive-content">
<div class="childPage">
<h1>This is the Page 3</h1>
<p>
This page was loaded using AJAX. If you can see the YUI-ext Button below then the javascript loaded in the parent page is accessible in this page as well.
</p>
<p>
<div id="myButton" style="margin-left: 5px;"></div>
</p>
</div>
</div>
</div>


But it simply results in a blank page.

manugoel2003
05-02-2007, 08:38 AM
A little adjustment and the borderLayout is working. This is the adjustment


Remove the class from the "container" div in the child page
Set "position: relative" in the css for "dynPageContainer" div


But the child borderLayout is not resizing properly, any ideas why?? and any suggestions to improve it?

davidelias
05-02-2007, 11:06 AM
A little adjustment and the borderLayout is working. This is the adjustment


Remove the class from the "container" div in the child page
Set "position: relative" in the css for "dynPageContainer" div


But the child borderLayout is not resizing properly, any ideas why?? and any suggestions to improve it?

Don't no if you already doing and i've never tried, but i think you must do something like this:
mainLayout.add('center'/* or wathever region*/, new Ext.NestedLayoutPanel(childLayout))
check the nested layout example (http://www.extjs.com/deploy/ext/examples/layout/nested.html)

manugoel2003
05-02-2007, 04:59 PM
No, I dont mean to add Nested Panels like that..... if I did, then why would I complicate the process.... I need this method when I need to have an Iframe like functionality but I dont want to re-download all the library files in the Iframe..... instead, why not use a DIV to load the page and re-use the libs....

I am just asking for a feedback as to whether this method can be used in place of the Iframe method described above to reduce complexity and yet maintain the functionality..... I would be posting a working link soon

manugoel2003
05-04-2007, 09:08 AM
I have moved my solution to its own thread with links to a couple of working samples
http://extjs.com/forum/showthread.php?t=5712

Samples

http://e11online.com/config_mgr/playpen/parent1.htm
http://e11online.com/config_mgr/playpen/parent2.htm