A better include function for JavaScript
2009/06/13

The common way of including a JavaScript file in a web application is to use the <script>-tag, but what if you want to load a script from another script? There are frameworks and some smaller simple include functions, which essentially create a <script>-tag dynamically. But there is a problem with all the functions I know: If you include multiple scripts, you can't be sure if they are loaded in the order you included them. In fact you can be almost sure, that this won't happen. So if the second included script relies on functions in the first included script you app will most likely break. This was the reason I created a new and better include function which solves several problems of existing js include solutions.

Features

  • includes will be loaded in the order they are mentioned in the script
  • there is a timeout: if a script does not load, it will be skipped
  • a callback function, which is called when the script has loaded, can be defined
  • if you try to include a script a second time and specify a callback function, only the callback function will be executed
  • especially for Titanium apps it is possible to include not only .js but also .py (Python) and .rb (Ruby) scripts
  • works with standard compliant browsers (Webkit-based, Gecko-based, Opera)

The Code

JavaScript Include Function

var include = (function(){
    var scripts = [];
    var curr = 0;
    var currScriptElem = null;
    var loadTimeout = null;
    var busy = false;
    var inited = false;
    var head = null;
 
    window.addEventListener('load',init,false);
 
    function init(){
        head = document.getElementsByTagName('head')[0];
        inited = true;
        next();
    };
 
    function next(e){
        busy = true;
        if(e){
            window.clearTimeout(loadTimeout);
            e.target.removeEventListener('load',next);
            var callbacks = scripts[curr-1].callbacks;
            scripts[curr-1].loaded = true;
 
            if(callbacks.length > 0){
                var i = callbacks.length;
                while(i--){
                    callbacks[i]();
                }
            }
        }
 
        if(scripts.length > curr){
            var currentScript = scripts[curr].path;
            curr++;
 
            var script = document.createElement('script');
            var suffix = currentScript.substring(currentScript.lastIndexOf('.')+1);
            var type;
            switch(suffix){
                case 'js': type ='text/javascript';break;
                case 'rb': type ='text/ruby';break;
                case 'py': type ='text/python';break;
                case 'php': type ='text/php';break;
                default: type = 'text/javascript';
            }
 
             script.setAttribute('type', type);
             script.setAttribute('src', currentScript);
            script.addEventListener('load',next,false);
 
            currScriptElem = script;
            loadTimeout = window.setTimeout(skip,10000);
            head.appendChild(script);
 
        } else {
            busy = false;
        }
    };
 
    function skip(){
        currScriptElem.removeEventListener('load',next);
        head.removeChild(currScriptElem);
        next();
    };
 
    function include(scriptPath,optCallback){
        var dup = false;
        var i = scripts.length;
        while (i--) {
            if(scripts[i].path == scriptPath){
                dup = true;
                if (optCallback) {
                    if(scripts[i].loaded){
                        optCallback();
                    } else {
                        scripts[i].callbacks.unshift(optCallback);
                    }
                }
                break;
            }
        }
 
        if(!dup){
            var cbs = [];
            if(optCallback) cbs.unshift(optCallback);
            scripts.push({path:scriptPath,callbacks:cbs,loaded:false});
        }
 
        if(!busy && inited) next();
    };
    return include
})();

Usage

The include function is very easy to use. The callback function is optional.

Usage Example

// simple
include('includetest.js');
 
// with callback
include('includetest2.js',function(){console.log('include 2 callback');});

Please comment, if you have ideas of how to improve the function.

Discussion

Bryan Snyder, 2009/11/26 09:36

I added this to your script to allow for firefox to use the script locally

try {

  try {
imageData=myCanvasContext.getImageData(0,0, imgWidth, imgHeight);
  } catch (e) {
    netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
  imageData=myCanvasContext.getImageData(0,0, imgWidth, imgHeight);
  }
} catch (e) {
  throw new Error("unable to access image data: " + e);
}
Enter your comment (wiki syntax is allowed):