Page 1 of 2 12 LastLast
Results 1 to 10 of 14

Thread: ext.ux.JSLoader - load JavaScript on demand

  1. #1
    Ext User
    Join Date
    Sep 2008
    Posts
    42
    Vote Rating
    0
      0  

    Post ext.ux.JSLoader - load JavaScript on demand

    Due to several problems with the various ways I've seen others trying to dynamically load additional classes and run external JavaScript on the fly, I decided to write my own.

    Code:
    Ext.ux.JSLoader = function(options) {
      
      Ext.ux.JSLoader.scripts[++Ext.ux.JSLoader.index] = {
        url: options.url,
        success: true,
        options: options,
        onLoad: options.onLoad || Ext.emptyFn,
        onError: options.onError || Ext.ux.JSLoader.stdError
      };
      
      Ext.Ajax.request({
        url: options.url,
        scriptIndex: Ext.ux.JSLoader.index,
        success: function(response, options) {
          var script = 'Ext.ux.JSLoader.scripts[' + options.scriptIndex + ']';
          window.setTimeout('try { ' + response.responseText + ' } catch(e) { '+script+'.success = false; '+script+'.onError('+script+'.options, e); }; if ('+script+'.success) '+script+'.onLoad('+script+'.options);', 0);
        },
        failure: function(response, options) {
          var script = Ext.ux.JSLoader.scripts[options.scriptIndex];
          script.success = false;
          script.onError(script.options, response.status);
        }
      });
      
    }
    
    Ext.ux.JSLoader.index = 0;
    Ext.ux.JSLoader.scripts = [];
    
    Ext.ux.JSLoader.stdError = function(options, e) {
      window.alert('Error loading script:\n\n' + options.url + '\n\n(status: ' + e + ')');
    }
    The following simple example demonstrates how to use this, and shows the prototype for the onLoad and onError callbacks:

    Code:
    Ext.onReady(function(){
      new Ext.ux.JSLoader({
        url: '/scripts/my_script.js',
        onLoad: function(options) { alert('Script Loaded'); },
        onError: function(options, e) { alert('Error loading script'); }
      });
    });
    The events are both optional.

    You may prefer not to use the onLoad callbacks, and instead just place the code to execute in the loaded script - if the loaded script contains classes, that may not be the most elegant way to go about it, hence this event is available.

    If no onError event is provided, a default error handler will display an alert with the URL and a description of the error.

    The error handler will catch exceptions, so if your loaded script contains syntax errors, you will get an onError event. You will also get an onError event (with an error code, e.g. 404) if the HTTP request failed.

    Your loaded script executes in the global scope, which means you can use it to load extensions or other classes on the fly.

  2. #2
    Ext User
    Join Date
    Jul 2008
    Posts
    1
    Vote Rating
    0
      0  

    Default

    Good job!

  3. #3
    Sencha User jay@moduscreate.com's Avatar
    Join Date
    Mar 2007
    Location
    DC Area =)
    Posts
    16,364
    Vote Rating
    87
      0  

    Default

    This isgreat. I love that you have it event based.

  4. #4
    Ext User
    Join Date
    Feb 2008
    Posts
    11
    Vote Rating
    0
      0  

    Lightbulb

    Thanks Mindplay!

    I modified it some so that it can be used to load ext components (dynamically generated if you like) on the fly.

    JSLoader.js
    Code:
    Ext.ux.JSLoader = function(options) {
     
      Ext.ux.JSLoader.scripts[++Ext.ux.JSLoader.index] = {
        url: options.url,
        success: true,
        jsLoadObj: null,
        options: options,
        onLoad: options.onLoad || Ext.emptyFn,
        onError: options.onError || Ext.ux.JSLoader.stdError
      };
     
      Ext.Ajax.request({
        url: options.url,
        params: options.params,
        scriptIndex: Ext.ux.JSLoader.index,
        success: function(response, options) {
          var script = Ext.ux.JSLoader.scripts[options.scriptIndex];
          try {
              script.jsLoadObj = Ext.decode(response.responseText);
              Ext.applyIf(script.jsLoadObj,{jsLoad: function(){return Ext.ComponentMgr.create(script.jsLoadObj);}});
              var comp = script.jsLoadObj.jsLoad();
              if (comp.remoteInit){
                  comp.remoteInit();
              }
          } catch(e) {
              script.success = false;
              script.onError(script.options, e);
          }
          if (script.success) script.onLoad(comp,script.options);
        },
        failure: function(response, options) {
          var script = Ext.ux.JSLoader.scripts[options.scriptIndex];
          script.success = false;
          script.onError(script.options, response.status);
        }
      });
    }
     
    Ext.ux.JSLoader.index = 0;
    Ext.ux.JSLoader.scripts = [];
     
    Ext.ux.JSLoader.stdError = function(options, e) {
     // throw(e);
      window.alert('Error loading script:\n\n' + options.url + '\n\nstatus: ' + e);
    }
    example usage
    Code:
            new Ext.ux.JSLoader({
                url: 'dynamic_comp.cfm', //You can generate components dynamically, I use ColdFusion
                params: {foo: 'bar'},
                //put anything else you will want to pass to the onLoad function here like
                closable: 1,
                onLoad:function(comp, options){
                     //your component will be delivered here
                     //do what you want with it like at it as a tab to a tabpanel
                    getMainPanel().add({
                      title: 'New Tab'
                      closable: options.closable,  //the options contain everything that was defined in your JSLoader config
                      border: false,
                      layout: 'fit',
                      items: [comp]
                       }).show();
                    getMainPanel().doLayout();
                }
            });
    There are two was to return the components. One using the lasy render with xtype or the other is generating the components by using new and returning it. I will show both ways

    First Way

    dynamic_comp.cfm xtype Way
    Code:
               {
    	    xtype: 'panel',
    	    layout: 'column',
    	    title:'Dashboard',
    	    layoutConfig: {
    	        // The total column count must be specified here
    	        columns: 1
    	    },
                    //This is not required but if it is defined, it will be called by the JSLoader in the scope of this panel
                    remoteInit: function(){
                        this.title = 'Im Alive';
                    }
    	}
    This is easy enough.

    Second Way

    Return an object with the defined function jsLoad() and return the component.
    JSLoader will call the jsLoad function if it exists and pass the component you return to the onLoad method you defined in the JSLoader config

    dynamic_comp.cfm Function Way
    Code:
    {
    	jsLoad: function(){
    
                       var subPanel = new Ext.Panel({
                            title: 'Sub Panel'
                       })
                       
                       var thePanel = new Ext.Panel({
                           title: 'Panel 1',
                           items: [subPanel]
                       })
    
                       return thePanel;
                }
    }
    Hope this helps someone.

  5. #5
    Sencha Premium Member Ronaldo's Avatar
    Join Date
    Jul 2007
    Location
    Enschede, The Netherlands
    Posts
    299
    Vote Rating
    4
      0  

    Default

    Hi!

    Wauw, I haven't tried it but I surely will!
    Please consider adding this to the ux extension repository, so we all won't need to look search the forum for the latest code if you haven't done this already.

    Ronaldo

  6. #6
    Ext User
    Join Date
    Sep 2008
    Posts
    42
    Vote Rating
    0
      0  

    Default

    The whole thing went to through major rewrites on my end since that post.

    The first rewrite used Events, not just callbacks, and supported dynamically loading components, respected the order in which you loaded things (e.g. guaranteed that you could load a components and a subclass in the correct order), and supported JS and CSS. It was very fast, because it fired up as many simultaneous requests as the browser would allow, and although these might finish out of order, it would inject the scripts in the order you requested them.

    In my second rewrite, I'm back to using callbacks. It turns out that using events actually does not make much sense, since the load() event could happen at any point after object creation - you might not have time to attach another listener and actually be guaranteed that the load() event hasn't already passed you by.

    I also went from injecting <script>...</script> tags, to the more traditional <script src="..."></script> method instead. I chose this approach because injecting the code makes it impossible to debug your scripts - you can no longer see which file the code came from, which in my case was pretty much disasterous; we're loading some 30-odd different scripts on-demand, and will be loading somewhere around 50-100 different scripts when this project is done, so tracing an error was a real pain...

    Checking if the <script> and <link> tags are fully loaded turned out to be quite an interesting cross-browser challenge, but was worked out fully for FF2-3, IE6+, Opera, Chrome (tested on Safari and FF3 on Mac as well).

    Unfortunately, the onError event no longer exists in the last implementation, as there is no cross-browser way (only IE can do it) to see if a <script> or <link> tag failed to load.

    But it has all of the other features mentioned for the second implementation, plus an extra method a'la PHP's require_once() that loads a script only once, but still respects the order in which you requested the resources.

    Another small drawback is the somewhat lower speed, compared to the second rewrite. There is not a lot I can do about that.

    I'm negociating with my boss, that we publish the final script as open source. It will most likely be available, with an article, on our blog at some point.

    I will post here when that happens...

  7. #7
    Sencha User nouveauc's Avatar
    Join Date
    Jan 2008
    Location
    i'm live to Ivoiry Cost(C
    Posts
    19
    Vote Rating
    0
      0  

    Default

    great jobs. I think a system of desktop based on qwikioffice destop but with dynamic js,css or component loaders;fonction include(async) or require(sync).I want to know your opinions

  8. #8
    Ext User
    Join Date
    Jul 2007
    Location
    Florida
    Posts
    9,996
    Vote Rating
    8
      0  

    Default

    As far as how you treat the returned resource, whether you eval it or include it to the head etc., I just made a config option so you tell the class how to handle the response. So if you're debugging, you just switch your toggle to include the file in the head, if you want to use eval you toggle for that. I also was implementing such a toggle in case someone thought they were over exposing themselves to whatever cross site scripting attack.

  9. #9
    Sencha User
    Join Date
    Mar 2008
    Posts
    566
    Vote Rating
    0
      0  

    Default

    mindplay,

    do you have the latest version of this code? Are you able to release it and how. I could really use this in my current project.

    Thanks, Marty

  10. #10
    Touch Premium Member
    Join Date
    Aug 2009
    Posts
    28
    Vote Rating
    0
      0  

    Default

    hmm... i tried using this to load 3 scripts in a row:

    1. TinyMCE
    2. Ext.ux.ManagedIFrame
    3. Ext.ux.TinyMCE

    and right after those 3 calls of the JSLoader, i wanted to use the Ext.ux.TinyMCE...

    But unfortunately already the loading of the Ext.ux.TinyMCE script failed because he did not know anything about the core TinyMCE objects...

    After a while I found out that this maybe is because the TinyMCE script is so large, and this is somehow too much for being loaded on the fly... because when i fired this function a few times in a row, it worked after the third time ;-) So, this is not really made for such large scripts i guess...

    Does anyone know how to load such large scripts on-the-fly for sure?
    I'm not so keen on forcing everybody to load the whole TinyMCE script right from the beginning, even if he is never using an editor...

Page 1 of 2 12 LastLast

Posting Permissions

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