Created
May 4, 2014 02:06
-
-
Save arkitrave/84089ba5f8eb41262d22 to your computer and use it in GitHub Desktop.
Require wrapper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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