While developing large ExtJS applications, we often face a problem of including all the Javascript files together in the index file which sometimes leads to a really long loading time. I faced this problem when I was developing the ExtJS desktop application with around 25 different modules and 100s of Javascript and CSS files. It was not really possible to include all those files in the index file and moreover, it was really unnecessary to include all module files when each module will load only after a user event. So, a few core files along with the Ext library files were loaded at start and the rest of the files are dynamically invoked depending on the module type. I wrote a simple Script Manager to handle this dynamic loading of JS and CSS files.
Here is the class :
/** * Script Manager class */ Ext.ux.ScriptManager = Ext.extend(Ext.util.Observable, { // The timeout in seconds to be used for requests timeout : 30, /** * @private * Array which will hold the scripts */ scripts : [], // Whether to cache the javascript files or not disableCaching : false, /** * @constructor * * Component constructor */ constructor: function(config){ Ext.apply(this, config); // Call our superclass constructor to complete construction process. Ext.ux.ScriptManager.superclass.constructor.call(this, config) }, /** * Accepts the config for loading Javascript files * @param {Object} o Config options */ loadJs : function(o){ if (!o) { return; } if (o.debug) { this.addAsScript(o); return; } if (!Ext.isArray(o.scripts)) { o.scripts = [o.scripts]; } o.url = o.scripts.shift(); if (o.scripts.length == 0) { this._loadUrl(o); } else { o.scope = this; this._loadUrl(o, function() { this.loadJs(o); }); } }, /** * Loads the css files dynamically * * @param {Object} o Config options - * {Array} scripts Array of css file paths | * {String} id Any existing css file with this id will be overwritten by the new file | * {Function} callback Function to be called once the files are loaded | * {Object} scope On this scope the callback function will be called * * @returns void */ loadCss : function(o) { var id = o.id || ''; var file; if (!Ext.isArray(o.scripts)) { o.scripts = [o.scripts]; } for (var i = 0; i < o.scripts.length; i++) { file = o.scripts[i]; id = '' + Math.floor(Math.random() * 100); Ext.util.CSS.createStyleSheet('', id); Ext.util.CSS.swapStyleSheet(id, file); } if(o.callback && Ext.isFunction(o.callback)){ o.callback.createDelegate(o.scope || window).call(); } }, /** * Adds the JS and CSS files as respective tags in DOM. This feature is used in debug:true option * @param {Object} o Config options * @returns void */ addAsScript : function(o) { var count = 0; var script; var files = o.scripts; if (!Ext.isArray(files)) { files = [files]; } var head = document.getElementsByTagName('head')[0]; Ext.each(files, function(file) { script = document.createElement('script'); script.type = 'text/javascript'; if (Ext.isFunction(o.callback)) { script.onload = script.onreadystatechange = function() { count++; if (count == files.length) { o.callback.call(); } } } script.src = file; head.appendChild(script); }); }, /** * @private * * Sends the AJAX request for loading the Javascript file * @param {String} url Url of the file to be loaded or the * config object with array of urls ans other config options * * @param {Function} callback Callback function which * will be called once all the files are loaded * * @returns Null */ _loadUrl : function(url, callback) { var cfg, callerScope; // If if (typeof url == 'object') { // must be config object cfg = url; url = cfg.url; callback = callback || cfg.callback; callerScope = cfg.scope; if (typeof cfg.timeout != 'undefined') { this.timeout = cfg.timeout; } if (typeof cfg.disableCaching != 'undefined') { this.disableCaching = cfg.disableCaching; } } // If the url exists in the scripts array, then call the callback function // This works as an recursive function call for multiple files if (this.scripts[url]) { if(callback && Ext.isFunction(callback)){ callback.createDelegate(callerScope || window).call(); } return null; } // Ajax request for loading the file Ext.Ajax.request({ url : url, success : this.processSuccess, failure : this.processFailure, scope : this, timeout : (this.timeout * 1000), disableCaching : this.disableCaching, argument : { 'url' : url, 'scope' : callerScope || window, 'callback' : callback, 'options' : cfg } }); return null; }, /** * @private * Function will be called if Ajax loading of scripts are successfull * @param {Object} response Ajax response object which will contain the script file content * @returns void */ processSuccess : function(response) { this.scripts[response.argument.url] = true; window.execScript ? window.execScript(response.responseText) : window .eval(response.responseText); if (response.argument.options.scripts.length == 0) { } if (typeof response.argument.callback == 'function') { response.argument.callback.call(response.argument.scope); } }, /** * @private * Function will be called if Ajax loading of scripts fails. It shows an error alert * @param {Object} response Ajax response object which will contain the script file content * @returns void */ processFailure : function(response) { Ext.MessageBox.show({ title : 'Application Error', msg : 'Script library could not be loaded.', closable : false, icon : Ext.MessageBox.ERROR, minWidth : 200, buttons: Ext.Msg.OK }); setTimeout(function() { Ext.MessageBox.hide(); }, 2000); } }); // Create an instance of the Script Manager ScriptMgr = new Ext.ux.ScriptManager();
And it can be used this way:
For loading Javascript files,
ScriptMgr.loadJs({ scripts : ['file1.js', 'file2.js'], debug : false, callback : function(){ console.log('loaded'); } });
“scripts” can be an array of file paths or a string of single file path. “debug” true/false denotes whether the file can be debugged in firebug or not. Actually, while getting the content of the Javascript file by a XHR request, the code is executed either by window.execScript() or window.eval() function. Both these function will not list the file data in script section of firebug. But, if the file is added in DOM as a tag then it will be listed and any error can be seen in the firebug console by line number. However, debug true/false can be used either way without any side effect. The callback function is called only when all the files are loaded properly. You can see the XHR requests for loading JS file in Firebug while debug=false:

CSS files can be loaded in similar way:
ScriptMgr.loadCss({ scripts : ['path/to/file1.css', 'path/to/file2.css'] });
Dynamic loading of css file is not generally needed in a web application, but for specific cases where there are lots of CSS files, this functionality may be used. It also supports overriding an existing css by “id”. You can load a css file with a specific id. And then if you load another css file (or the same css file) with that id, the old file will be overriddedn Here is an example:
Load a css file file1.css with id file_to_be_overridden:
ScriptMgr.loadCss({ scripts : 'path/to/file1.css', id : 'file_to_be_overridden' });
And then load another file file2.css with same id:
ScriptMgr.loadCss({ scripts : 'path/to/file2.css', id : 'file_to_be_overridden' });
The file1.css file will be exluded and the file2.css will be included in the DOM.
Sometime we need to load a component only after user does some actions – for example, we want to open a window once user clicks a button. And this window is a pretty heavy component with lots of functionality and a large JS file. So, we can load that window dynamically this way:
Ext.onReady(function(){ var panel = new Ext.Panel({ renderTo : document.body, title : 'My Panel', html: 'Hello World!', width : '100%', height : 400, tbar : [{ // A toolbar button with open window handler text : 'Open Window', scope : this, handler : function(){ ScriptMgr.loadJs({ scripts : 'LargeWindowComp.js', callback : function(){ // The window file is surely loaded now. // We can create the window instance var win = new LargeWindowComp({ width : 400, height : 300, title : 'I am loaded now' }); win.show(); } }); } }] }); });
I will update a jQuery version of this manager soon. Its very easy to load a JS file with ExtJS or jQuery but keeping a Script Manager at the top of your application makes things really easy and fast.
You can download all the source codes from here.






Recent Comments