1
\$\begingroup\$

I have a file HintBox.js containing a JS prototype of the same name which requires our internal JS library (call it $lib) in order to work. One way to solve this problem would be to remember to make sure that the lib.js file is included every time in the PHP, but this is easy to mess up, especially in big, complex web pages. The other problem that I'm trying to solve is the fact that relative links in a JS file are relative to the loading webpage, not the src of the script executing.

The following code seems to solve the problem, but I'd like to know if anyone can think of corner cases where it wouldn't work, or older versions of IE that would give me trouble. (Only really care about IE 8+, but I'd be interested to know if it would work in 6/7 too.) And of course, anything else that you think needs improving:

if(typeof HintBox === "undefined") {

    var HintBox = (function() {

        // load lib.js if necessary
        var scripts, i, pos, pathToLibJS;
        if(typeof $lib === "undefined") {
            scripts = document.getElementsByTagName("script");
            for(i = 0; i < scripts.length; i++) {
                if(scripts[i].src) {
                    pos = scripts[i].src.indexOf("HintBox.js");
                    if(pos !== -1) {
                        pathToLibJS = scripts[i].src.substring(0, pos) +
                            (pos === 0 ? "/" : "") + "../lib.js";
                        document.write("<script type=\"text/javascript\" src=\"" + 
                            pathToLibJS + "\"><" + "/" + "script>");
                        break;
                    }
                }
            }
        }

        var HintBox = function (inputElement, hintMsg, hintColor) {
            // do stuff
        };

        HintBox.prototype.func = function(params) {
            // do more stuff
        };

        return HintBox;

    })();

}

The one thing that I don't like about this approach is that the code which grabs lib.js is not encapsulated in a function. If I did have a function, say called retrieveScript(curFilePath, relPathToExtFile), which grabbed the code, the file that it was stored in would have to be retrieved in a similar way, so it would be a circular solution. The ideal would be to use a CMS with modules that handled dependencies like this for us, but that's not something we have right now.

Update: I've tried to make it as easy to copy and paste from script to script as possible:

(function (currentScriptFileName) {
    var dependencies, scripts, i, pos, j, baseDir;
    // get dependencies (variable number of args)
    dependencies = Array.prototype.slice.apply(arguments, 1);
    // find path to current script
    scripts = document.getElementsByTagName("script");
    for(i = 0; i < scripts.length; i++) {
        if(scripts[i].src) {
            pos = scripts[i].src.indexOf(currentScriptFileName);
            if(pos !== -1) {
                baseDir = scripts[i].src.substring(0, pos) + (pos === 0 ? "/" : "");
                break;
            }
        }
    }
    // return if not found
    if(!baseDir) return;
    // load dependencies
    for(j = 0; j < dependencies.length; j++) {
        if(dependencies[j]) {
            document.write("<script type=\"text/javascript\" src=\"" 
                + baseDir + dependencies[j]+ "\"><" + "/" + "script>");
        }
    }
})("HintBox.js", typeof $lib === "undefined" ? "lib.js" : null);

As you can see, the first argument is the current script file name, and the arguments following are the dependencies. We account for dependencies that have already been loaded by testing in the arguments to the function:

typeof $lib === "undefined" ? "lib.js" : null

And then the function ignores any falsy dependencies. If it were more widespread I would have used the ... operator instead of Arguments.prototype.slice.apply.

Last update:

What I decided to do instead is have a PHP function that you call like so:

<?php
requireJS(__FILE__);
?>

which inserts a script tag for my require.js script, and then in each API script, I do

if(typeof APIName === "undefined") {

    if(window.$lib && $lib.require) {
        $lib.require("APIName.js", (!dependency ? "dependency.js" : null));
    }

    var APIName = (function() {

    // code goes here

    })();

}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.