Skip to content

Instantly share code, notes, and snippets.

@arkitrave
Created May 4, 2014 02:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arkitrave/84089ba5f8eb41262d22 to your computer and use it in GitHub Desktop.
Save arkitrave/84089ba5f8eb41262d22 to your computer and use it in GitHub Desktop.
Require wrapper
/* FOR REFERENCE PURPOSES ONLY. CODE IS AUTHORED BY KEVAN DAVIS, COPYRIGHT 2010-2014 GILT GROUPE. */
/*jshint asi: false, bitwise: true, boss: false, curly: true, eqeqeq: true, eqnull: false, evil: false, forin: false, immed: true, laxbreak: false, newcap: true, noarg: true, noempty: true, nonew: false, nomen: false, onevar: true, plusplus: false, regexp: false, undef: true, sub: false, strict: false, white: false */
/*jshint browser: true, maxerr: 50, passfail: false */
/*global define: false */
/**
* requirejs wrapper
*
* will use either requirejs or global variables based on several factors,
* such as the existence or absence of requirejs, config values, and query string parameters.
*/
(function (root) {
if (root.createModule) {
return;
}
var
PATH_SEPARATOR = ".",
require = root.require,
define = root.define,
targetExperience = root.targetExperience || 'minimal',
manifest, // Local cache of manifest.json, loaded on demand
namedBundleLookupTable = root.tmpRequireModulesNamedBundleLookupTable || {},
loadedBundles = [],
testFunction,
isMerged = false,
isMinified = false;
// Killing the native require, so that vendor code will work with require_wrapper
if (!window.__karma__) { // Forgive me father, for I have sinned...
root.require = undefined;
root.define = undefined;
}
if (root.tmpRequireModulesNamedBundleLookupTable) { // this var ONLY exists when we are merged.
isMerged = true; // therefore, we we know we're merged
root.tmpRequireModulesNamedBundleLookupTable = undefined;
testFunction = function () { var foo = "bar"; }; // when minified, 'foo' should get renamed
if (testFunction.toString().indexOf('foo') === -1) { // and therefore, won't be found here
isMinified = true; // therefore, we know we're minified
}
}
// _.isString
function isString (obj) {
return "[object String]" === Object.prototype.toString.call(obj);
}
// _.isFunction
function isFunction (obj) {
return "[object Function]" === Object.prototype.toString.call(obj);
}
// _.isUndefined
function isUndefined (obj) {
return obj === void 0;
}
// Not a true indexOf; minimal version just for our needs here
function hasIndexOf (arr, item) {
var i;
for (i = 0; i < arr.length; i += 1) {
if (arr[i] === item) {
return true;
}
}
return false;
}
/**
* Normalize the array of dependencies as either:
*
* @param {Array} deps
* @returns {Array}
*/
function processDependencies (deps) {
var dependencies = [], i, tmp;
for (i = 0; i < deps.length; i += 1) {
tmp = deps[i];
tmp = tmp.replace(/\./g, "/");
// Shorthand notation, ex: instead of "vendor/jquery/jquery", you can just do "vendor/jquery"
if (2 === tmp.split("/").length && "src" !== tmp.split("/")[0] && "config" !== tmp.split("/")[0]) {
tmp += "/" + tmp.split("/")[1];
}
dependencies.push(tmp);
}
return dependencies;
}
function interceptNamedBundleDependencies (deps, callback) {
var
bundle, bundles = [], downloaded = 0,
foundInBundles = false,
i, j, url, reqContext;
// If we are merged (or minified)
if (isMerged) {
// Iterate over all of the deps that are being requested
for (i = 0; i < deps.length; i += 1) {
// Is this dep in a named bundle?
bundle = namedBundleLookupTable[deps[i]];
// If so...
if (bundle) {
foundInBundles = false;
// Check to see if we are already planning to download it
for (j = 0; j < bundles.length && !foundInBundles; j += 1) {
if (bundles[j] === bundle) {
foundInBundles = true;
}
}
// If not (if it's new for this request)
if (!foundInBundles) {
foundInBundles = false;
// Check to see if it is was downloaded previously
for (j = 0; j < loadedBundles.length && !foundInBundles; j += 1) {
if (loadedBundles[j] === bundle) {
foundInBundles = true;
}
}
// If not (if it really is a new request)
if (!foundInBundles) {
// Add it to our list of bundles to download.
bundles.push(bundle);
}
}
}
}
// If we have any bundles that we need to download
if (bundles.length) {
reqContext = {
contextName: '_',
scriptCount: 0,
config: {},
// Create a callback that will run when each bundle downloads
onScriptLoad: function () {
downloaded += 1; // Increment a counter
if (downloaded === bundles.length) { // When everything is downloaded
// require is async by nature, so, we need to wait a moment before calling it
setTimeout(function () {
callback(deps); // Let require take over
}, 0);
}
}
};
// require.js has Several require.load methods
// I'm not entirely sure which ones we're Actually using
// One of them uses onScriptLoad, the others use completeLoad
reqContext.completeLoad = reqContext.onScriptLoad;
// Iterate over all of the bundles we need to download
for (i = 0; i < bundles.length; i += 1) {
// Get the correct url to download this bundle at, taking target experience and minification into account
url = bundles[i] + '.' + targetExperience + '.' + (isMinified ? 'min' : 'src') + '.js';
// And fetch the bundle, using an (internal) require.js method to handle x-browser differences
if (require.attach) {
require.attach(require.toUrl(url), reqContext, bundles[i] + '-bundle', reqContext.onScriptLoad);
} else {
require.load(reqContext, bundles[i] + '-bundle', require.toUrl(url));
}
// And mark this bundle as having been downloaded already.
loadedBundles.push(bundles[i]);
}
} else {
callback(deps); // It's not in a bundle, so, lazy load it via require
}
} else {
callback(deps); // We're not merged, so, just go ahead and require it
}
}
/**
* Create a module using requirejs
*
* @public
*
* @param {Object} settings
* or
* @param {String} module_name The module name, in the format 'group_name.module_name'
* @option {Array} dependencies Dependencies of the module to be created
* @param {Function} constructor The module to be created
* @option {Object} options An options hash, can contain targetExperiences[]
*/
root.createModule = function createModule (settings) {
var tmp, i, ref;
// Support the same syntax requirejs uses
if (1 < arguments.length) {
settings = {
name: arguments[0]
};
if (isFunction(arguments[1])) {
settings.dependencies = [];
settings.constructor = arguments[1];
settings.options = arguments[2] || {};
} else {
settings.dependencies = arguments[1];
settings.constructor = arguments[2];
settings.options = arguments[3] || {};
}
}
// Only define if it's targeted to the right target experience level or doesn't specify
if ((settings.options.targetExperiences && hasIndexOf(settings.options.targetExperiences, targetExperience)) || !settings.options.targetExperiences) {
settings.dependencies = processDependencies(settings.dependencies || []);
// TODO: simplify this to just settings.name (once no one is using hash notation anymore)
tmp = isString(settings.name) ? settings.name : settings.name.amd;
tmp = tmp.replace(/\./g, "/");
// Shorthand notation, ex: instead of "vendor/jquery/jquery", you can just do "vendor/jquery"
if (2 === tmp.split("/").length && "src" !== tmp.split("/")[0] && "config" !== tmp.split("/")[0]) {
tmp = tmp + "/" + tmp.split("/")[1];
}
// If this module is not already defined...
if (!require.s.contexts._.defined[tmp]) {
// ...define it
define(tmp, settings.dependencies, settings.constructor);
}
}
};
root.createConfig = function createConfig (key, value) {
(define || createModule)(key.replace(/\./g, "/"), function () { return value; });
};
/**
* Call callback after loading all dependencies
* Dependencies are either loaded using requirejs, or evaluated globally.
*
* @public
*
* @param {Array} dependencies
* @param {Function} callback
*/
root.requireModules = function requireModules (dependencies, callback) {
interceptNamedBundleDependencies(dependencies, function (deps) {
require(processDependencies(deps), callback);
});
};
/**
* Specific wrapper for specs. Require 2.0 doesn't have a 'done' flag. This wrapper provides one.
* Call callback after loading all dependencies
* Dependencies are either loaded using requirejs, or evaluated globally.
*
* @public
*
* @param {Array} dependencies
* @param {Function} callback
*/
root.requireSpecs = function requireSpecs (dependencies, callback) {
var deps = processDependencies(dependencies);
root.specsReady = false;
root.jasmineJquery = root.jasmineJquery || false;
if (!root.jasmineJquery) {
deps.every(function (dep) {
if (dep === 'vendor/jquery/jquery') {
root.jasmineJquery = true;
return false;
}
});
}
function requireSpecsCallback () {
root.specsReady = true;
callback && callback.apply(this, arguments);
}
if (root.jasmineJquery) {
require(['jasmine-jquery'], function (){
require(deps, requireSpecsCallback);
});
} else {
require(deps, requireSpecsCallback);
}
}
/**
* Loads and inits a module
*
* @public
* @method initModule
*
* @param {String} module Module name
* @param {Mixed[]} args Arguments that init takes
*/
root.initModule = function initModule (module, args) {
requireModules([module], function (m) {
m && m.init && m.init.apply(null, args || []);
});
};
/**
* Synchronously (fake) returns a module.
* For developer use only.
*
* @public
* @method requireModules.get
*
* @param {String} module Module name
*
* @return {Mixed}
*/
root.requireModules.get = function (module) {
module = module.replace(/\./g, "/");
// Shorthand notation, ex: instead of "vendor/jquery/jquery", you can just do "vendor/jquery"
if (2 === module.split("/").length && "src" !== module.split("/")[0] && "config" !== module.split("/")[0]) {
module = module + "/" + module.split("/")[1];
}
// karma-requirejs uses requirejs v2.0 and resume() doesn't exist in 2.0.
// our webapps currently use 1.0.7, of which swift makes use of .get()
require.s.contexts._.resume && require.s.contexts._.resume();
return require.s.contexts._.defined[module];
};
/**
* Is a given module already loaded?
* For developer use only.
*
* @public
* @method requireModules.isLoaded
*
* @param {String} module Module name
*
* @return {Boolean}
*/
root.requireModules.isLoaded = function (module) {
module = module.replace(/\./g, "/");
// Shorthand notation, ex: instead of "vendor/jquery/jquery", you can just do "vendor/jquery"
if (2 === module.split("/").length && "src" !== module.split("/")[0] && "config" !== module.split("/")[0]) {
module = module + "/" + module.split("/")[1];
}
return require.s.contexts._.defined[module] || false;
};
/**
* Logs (and if synchronously available, returns) the version of a module.
* For developer use only.
*
* @public
* @method requireModules.getVersion
*
* @param {String} module Module name
*/
root.requireModules.getVersion = function getVersion (module) {
if (manifest) {
console.log(module + '=' + manifest.modules[module]);
} else {
requireModules(['common.request', 'config.js_base_path'], function (request, jsBasePath) {
request.get(jsBasePath + 'manifest.json').then(function (json) {
manifest = json;
getVersion(module);
});
});
}
};
}(this));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment