Skip to main content

$script.js is an asynchronous JavaScript loader and dependency manager with an astonishingly impressive lightweight footprint. Like many other script loaders, $script.js allows you to load script resources on-demand from any URL and not block other resources from loading (like CSS and images). Furthermore, it's unique interface allows developers to work easily with even the most complicated dependencies, which can often be the case for large, complex web applications.

/*!
 * $script.js JS loader & dependency manager
 * https://github.com/ded/script.js
 * (c) Dustin Diaz 2014 | License MIT
 */

(function(name, definition) {
    if (typeof module != "undefined" && module.exports)
        module.exports = definition();
    else if (typeof define == "function" && define.amd) define(definition);
    else this[name] = definition();
})("$script", function() {
    var doc = document,
        head = doc.getElementsByTagName("head")[0],
        s = "string",
        f = false,
        push = "push",
        readyState = "readyState",
        onreadystatechange = "onreadystatechange",
        list = {},
        ids = {},
        delay = {},
        scripts = {},
        scriptpath,
        urlArgs;

    function every(ar, fn) {
        for (var i = 0, j = ar.length; i < j; ++i) if (!fn(ar[i])) return f;
        return 1;
    }
    function each(ar, fn) {
        every(ar, function(el) {
            fn(el);
            return 1;
        });
    }

    function $script(paths, idOrDone, optDone) {
        paths = paths[push] ? paths : [paths];

        var idOrDoneIsDone = idOrDone && idOrDone.call,
            done = idOrDoneIsDone ? idOrDone : optDone,
            id = idOrDoneIsDone ? paths.join("") : idOrDone,
            queue = paths.length;

        function loopFn(item) {
            return item.call ? item() : list[item];
        }

        function callback() {
            if (!--queue) {
                list[id] = 1;
                done && done();
                for (var dset in delay) {
                    every(dset.split("|"), loopFn) &&
                        !each(delay[dset], loopFn) &&
                        (delay[dset] = []);
                }
            }
        }

        setTimeout(function() {
            each(paths, function loading(path, force) {
                if (path === null) return callback();
                if (!force && !/^https?:\/\//.test(path) && scriptpath) {
                    path =
                        path.indexOf(".js") === -1
                            ? scriptpath + path + ".js"
                            : scriptpath + path;
                }

                if (scripts[path]) {
                    if (id) ids[id] = 1;
                    return scripts[path] == 2
                        ? callback()
                        : setTimeout(function() {
                              loading(path, true);
                          }, 0);
                }

                scripts[path] = 1;
                if (id) ids[id] = 1;

                create(path, callback);
            });
        }, 0);

        return $script;
    }

    function create(path, fn) {
        var el = doc.createElement("script"),
            loaded;

        el.onload = el.onerror = el[onreadystatechange] = function() {
            if ((el[readyState] && !/^c|loade/.test(el[readyState])) || loaded)
                return;
            el.onload = el[onreadystatechange] = null;
            loaded = 1;
            scripts[path] = 2;
            fn();
        };

        el.async = 1;
        el.src = urlArgs
            ? path + (path.indexOf("?") === -1 ? "?" : "&") + urlArgs
            : path;

        head.insertBefore(el, head.lastChild);
    }

    $script.get = create;

    $script.order = function(scripts, id, done) {
        (function callback(s) {
            s = scripts.shift();
            !scripts.length ? $script(s, id, done) : $script(s, callback);
        })();
    };

    $script.path = function(p) {
        scriptpath = p;
    };

    $script.urlArgs = function(str) {
        urlArgs = str;
    };

    $script.ready = function(deps, ready, req) {
        deps = deps[push] ? deps : [deps];
        var missing = [];
        !each(deps, function(dep) {
            list[dep] || missing[push](dep);
        }) &&
        every(deps, function(dep) {
            return list[dep];
        })
            ? ready()
            : !(function(key) {
                  delay[key] = delay[key] || [];
                  delay[key][push](ready);
                  req && req(missing);
              })(deps.join("|"));
        return $script;
    };

    $script.done = function(idOrDone) {
        $script([null], idOrDone);
    };

    return $script;
});