Kevnz
3/20/2012 - 8:36 PM

jQuery, YUI-style

jQuery, YUI-style

/*
 * Assuming requireJS or another AMD loader
 * The idea is to have each jQuery.require() call have its own copy of jQuery
 * and all loaded modules copied into it, that way each call has its own "sandbox"
 */
(function (require, define) {
    
    /*
     * Optionally define a global config
     * Configuration is basically tiered into three levels that overwrite each other in this order:
     * 1. An internal configuration object
     * 2. The configuration defined in jQuery.GlobalConfig
     * 3. The configuration provided in the jQuery.require() call
     */
    if (!jQuery.GlobalConfig) {
        jQuery.GlobalConfig = {};
    }
    
    /**
     * Creates a new jQuery.sub() and loads modules on it
     */
    jQuery.require = function () {
        var $ = this.sub(),
            /*
             * Accessing directly "window" or "document" is a bad idea because
             * the code can be wanted to reference the document inside an iframe
             * Avoid them and use $.config.win or $.config.doc
             */
            opts = {
                win: window,
                doc: document
            };
        
        /**
         * Each sub() has a require function that can be called at any time
         * and it will enhance that particular sub with extra modules
         * jQuery.require() is then only a wrapper for creating a new sub and calling that sub's require function
         */
        $.require = function (config, modules, callback) {
            var self = this;

            // Mimic requireJS optional arguments
            if (arguments.length === 2) {
                if (jQuery.isFunction(modules)) {
                    callback = modules;
                    if (jQuery.isArray(config)) {
                        modules = config;
                        config = {};
                    } else {
                        modules = [];
                    }
                }
            } else if (arguments.length === 1) {
                if (jQuery.isFunction(config)) {
                    callback = config;
                    modules = [];
                    config = {};
                } else if (jQuery.isArray(config)) {
                    modules = config;
                    config = {};
                }
            }
            
            // overwrite configuration options
            this.config = this.extend(opts, jQuery.GlobalConfig, config);
            
            require(opts, modules, function () {
                for (var i = 0, length = arguments.length; i < length; i++) {
                    arguments[i](self);
                }
                if (callback) {
                    callback(self);
                }
            });
            return this;
        };
        
        return $.require.apply($, arguments);
    };
    
    /**
     * Modules must always be functions that will be later called on each jQuery sub
     */
    jQuery.define = function () {
        var args = Array.prototype.slice.call(arguments),
            fn = args.pop();
        args.push(function () {
            return fn;
        });
        define.apply(this, args);
    };
    
}(curl, curl.define)); // for example, curl could be used as a AMD loader

jQuery.define('hello', function ($) {
    $.sayHi = function () { console.log('hi'); };
});

jQuery.require(['hello'], function ($) {
    console.log($ === jQuery); // false
    console.log($.require === jQuery.require); // false
    $.sayHi();
    
    var previousSayHi = $.sayHi;
    jQuery.require(['hello'], function ($) {
        console.log(previousSayHi === $.sayHi); // false
    });
});