Page 1 of 3 123 LastLast
Results 1 to 10 of 30

Thread: >1 iFrame: Dont want to re-reload all scripts!

  1. #1
    Ext User
    Join Date
    Mar 2007
    Posts
    161
    Vote Rating
    0
      0  

    Default >1 iFrame: Dont want to re-reload all scripts!

    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:

    Code:
    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
    Code:
    <html>
    <head>
        <title>Loader-Sample</title>
        <script></script>
    </head>
    <body>
        <div>Ladevorgang, bitte warten...</div>
    </body>
    </html>
    init.js
    Code:
    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
    Code:
    <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
    Code:
        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:

    Code:
    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:

    Code:
    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?

  2. #2
    Ext User
    Join Date
    Mar 2007
    Posts
    161
    Vote Rating
    0
      0  

    Default How to show...

    How to show full script tags in code examples?

  3. #3
    Sencha User jack.slocum's Avatar
    Join Date
    Mar 2007
    Location
    New York, NY
    Posts
    6,956
    Vote Rating
    20
      0  

    Default

    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.

  4. #4
    Ext User
    Join Date
    Mar 2007
    Posts
    161
    Vote Rating
    0
      0  

    Default Lol

    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.

  5. #5
    Sencha User jack.slocum's Avatar
    Join Date
    Mar 2007
    Location
    New York, NY
    Posts
    6,956
    Vote Rating
    20
      0  

    Default

    Sorry about that, you must have been writing while I deployed the anti-spam update.

  6. #6
    Ext User
    Join Date
    Mar 2007
    Posts
    161
    Vote Rating
    0
      0  

    Default

    Quote Originally Posted by jacksloc
    Sorry about that, you must have been writing while I deployed the anti-spam update.
    Lol yeah after the post with the real link i have read it.

  7. #7
    Ext User
    Join Date
    Mar 2007
    Posts
    161
    Vote Rating
    0
      0  

    Default Here is my final version

    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
    Code:
    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
    Code:
    <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
    Code:
    <!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.
    Code:
    <!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>

  8. #8
    Sencha User jack.slocum's Avatar
    Join Date
    Mar 2007
    Location
    New York, NY
    Posts
    6,956
    Vote Rating
    20
      0  

    Default

    I will be playing with this later, thanks for sharing!

  9. #9
    Ext User
    Join Date
    Mar 2007
    Posts
    161
    Vote Rating
    0
      0  

    Default Update

    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:

    Code:
    <script type="text/javascript">
            SH_NO_AUTOINJECT_STYLES = false;
    </script>
    Or you can inject the preloaded styles by calling:

    Code:
    <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.

    Code:
    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);
            };
        }
    }

  10. #10
    Ext User
    Join Date
    Mar 2007
    Posts
    161
    Vote Rating
    0
      0  

    Default Issues

    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

Page 1 of 3 123 LastLast

Similar Threads

  1. reload tree from iframe
    By Dextro in forum Ext 1.x: Help & Discussion
    Replies: 1
    Last Post: 22 Mar 2007, 12:07 PM
  2. IE failure, dont know what he wants :-P
    By humpdi in forum Ext 1.x: Help & Discussion
    Replies: 7
    Last Post: 8 Mar 2007, 8:14 AM
  3. grid: delete row using rowId (dont have index no.)
    By kinky_lizzard in forum Ext 1.x: Help & Discussion
    Replies: 2
    Last Post: 12 Jan 2007, 12:33 AM
  4. dm.createNode = function(doc, id, colData) dont work to me
    By genius551v in forum Ext 1.x: Help & Discussion
    Replies: 2
    Last Post: 6 Dec 2006, 7:55 PM
  5. reload Iframe when resizing COntentPanel
    By elpoj in forum Ext 1.x: Help & Discussion
    Replies: 2
    Last Post: 27 Oct 2006, 10:42 AM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •