src/define.js
// Defining anonymous and named modules for our library is done through this function. There are a number of ways to
// pass arguments into this function, for more details see comments in the `define.args` function.
/* eslint-disable*/
var define = function() {
/* eslint-enable*/
// Fetch and normalize the argument that were passed in.
var args = me.amd.args(arguments, define.args.router, define.args.normalize, {
name: null,
error: null,
deps: null,
factory: undefined
});
// Fill up our dependencies.
args = define.deps(args);
// If we're attempting to define an anonymous module, and we already have an anonymous module that was previously
// passed to the function before we had a chance to actually declare it's name, then there was 2 anonymous modules
// which were passed in through a single file. We cannot due this due to the fact that we have no way of knowing which
// name should be assigned to which `define` instance that was called. If we did allow this to go through, we would
// essentially be overwriting our previous anonymous module. If this happens we'll simply halt the function and a
// throw a notice to our end user.
if (!args.name) {
// We cannot define multiple anonymous modules with the same name!
if (define.anonymous.pending) {
me.log(1, 'define', 'Multiple Anonymous modules defined in the same file! Halting!', args);
return;
}
// If we don't have a name for our module, then we'll define it as an anonymous module. Our callback that loaded our
// file will see this and set the proper name once the callback executes, which will happen synchronously.
define.anonymous.save(args);
return;
}
// Generate our new module and reference it as our last defined module.
define.module.last = define.module(args);
// Flag the anonymous modules as not pending.
define.anonymous.pending = false;
};
// Whether or not to enforce the use of AMD. If this setting it turned on via the `config` `Function`, any library that
// supports AMD will not longer be available via the `window` `global`. See documentation for further details.
define.amd = undefined;
// If a module is sitting in an anonymous state and waiting to be imported properly, this `Function` will take the
// `dependencies` and `factory` from that anonymous module, import them in to our properly named module, and then
// destroy the anonymous module sitting in a limbo state. Here's how this system of defining anonymous modules works:
// If we attempt to load a module via our `require` function, and that module doesn't happen to exist in our
// configuration, we'll automatically spawn a new anonymous module for it. Once the file for the module has loaded, if
// the file contains an anonymous `define` call, we'll then store it in a limbo state with it's `factory` and
// `dependencies`. The issue is that the `define` function will be invoked before the `onload` callback from our
// script file that was loaded for our module, so we have to store it in a limbo state, and then after the callback
// executes, we then look to see if an anonymous module is waiting to be defined, if so, we then define it whatever
// anonymous module that's sitting in a limbo state with the name of the module that was given from our callback. If
// our callback provides a `define` function with a name, any anonymous modules sitting in a limbo state will be wiped
// out completely.
define.anonymous = function(moduleName) {
// If we don't have a anonymous module waiting to be defined, then halt the function. We should at the very least have
// either a `factory` or reference to the last defined module (which in this case would be our anonymous module
// without a name).
if (!define.anonymous.pending && !define.module.last) {
return;
}
// Fetch the instance of our module.
var module = me.module(moduleName, null, false);
// If we couldn't find our module, then something went wrong. Let the end user know and halt the `Function`.
if (!module) {
me.log(1, 'define', 'anonymous', 'Anonymous module not found for `' + moduleName + '`! Halting definition!');
return;
}
// If our module already exists and there's a module that's set as our last defined, then a file was loaded which the
// library assumed was anonymous, but wound up being explicitly calling the `define` `Function` with a `name`. In this
// particular case, we'll destroy the new definition and instead alias it with our anonymous module. The point in
// doing this is so that we only maintain a single reference. If we maintain multiple references there's a possibility
// of each of the references becoming out of sync.
if (module && define.module.last && define.module.last.name !== module.name) {
// Define the alias coming from the `define` function for the anonymous file that was loaded.
me.module.alias(module.name, [define.module.last.name]);
// Attempt to fetch the index of our anonymous module from our require library.
var anonymousIndex = me.indexOf(me.require.anonymous, moduleName);
// Reference the factory from the file that was loaded if the current factory is `undefined`.
if (anonymousIndex !== -1) {
// Remove the anonymous entry.
me.require.anonymous.splice(anonymousIndex, 1);
// Reference the factory.
module.factory = define.module.last.factory;
}
// Delete the actually module reference.
delete me.module.definitions[define.module.last.name];
} else {
// Set the dependencies for our anonymous `module`.
if (me.isDefined(define.anonymous.deps)) {
module.deps = define.anonymous.deps;
}
// Set the factory for our anonymous `module`.
if (me.isDefined(define.anonymous.deps)) {
module.factory = define.anonymous.factory;
}
}
// Reset the pending anonymous values waiting to be populated.
define.anonymous.reset();
};
// The dependencies for our anonymous `module` waiting to be properly defined.
define.anonymous.deps = undefined;
// The factory for our anonymous `module` waiting to be properly defined.
define.anonymous.factory = undefined;
// Flag whether or not an anonymous module is pending to be defined.
define.anonymous.pending = false;
// Clear out any saved anonymous `module` properties.
define.anonymous.reset = function() {
// Clear the pending state.
define.anonymous.pending = false;
// Clear the dependencies.
define.anonymous.deps = undefined;
// Clear the factory.
define.anonymous.factory = undefined;
};
// Store the anonymous module waiting to be defined.
define.anonymous.save = function(args) {
// Remove the reference for our last defined module.
define.module.last = null;
// Reset the previously saved values if present.
define.anonymous.reset();
// If the `args` parameter isn't an `Object`, halt the `Function`.
if (!me.isObject(args)) {
return;
}
// Flag the pending state.
define.anonymous.pending = true;
// Set the dependencies for our anonymous module.
if (me.isDefined(args.deps)) {
define.anonymous.deps = args.deps;
}
// Set the factory for our anonymous module.
if (me.isDefined(args.factory)) {
define.anonymous.factory = args.factory;
}
};
// Route and normalize the arguments that are passed into our `define` function. The arguments for our `define`
// `Function` can be sent in a number of different forms.
define.args = {};
// Normalize the arguments payload.
define.args.normalize = function(payload) {
// Normalize the `name` `String`.
payload.name = me.normalizeString(payload.name);
// Normalize the `error` `Function`.
payload.error = me.normalizeFunction(payload.error);
// Normamlize the `dependencies` `Array`.
payload.deps = payload.deps ? me.normalizeStringSeries(payload.deps) : null;
// Don't normalize the `factory`, as it can be anything except `undefined`.
// Return the noramlized `payload`.
return payload;
};
// Route the arguments passed into the `define` `Function`.
define.args.router = [];
// Handle no arguments being passed into the `define` `Function`.
define.args.router[0] = function(args, payload) {
// Throw an error to the end user.
me.log(1, 'define', 'args', 'No arguments were passed into `define`! Halting!', args);
// Return our factored payload.
return payload;
};
// Handle 1 argument being passed into the `define` `Function`.
define.args.router[1] = function(args, payload) {
// Reference the `factory`.
payload.factory = args[0];
// Return our factored payload.
return payload;
};
// Handle 2 arguments being passed into the `define` `Function`.
define.args.router[2] = function(args, payload) {
// If the first argument is a `String`, treat the arguments as `name`, and `factory`.
if (me.isString(args[0])) {
// Reference the `name`.
payload.name = args[0];
// Reference the `factory`.
payload.factory = args[1];
// If the first argument is an `Array`, treat the arguments as `dependencies`, and `factory`.
} else if (me.isaArray(args[0])) {
// Reference the `dependencies`.
payload.deps = args[0];
// Reference the `factory`.
payload.factory = args[1];
// If none of the criteria above matched, then the arguments are malformed.
} else {
me.log(1, 'define', 'args', '2 arguments were passed into `define` that were malformed! Discarding!', args);
}
// Return our factored payload.
return payload;
};
// Handle 3 arguments being passed into the `define` `Function`.
define.args.router[3] = function(args, payload) {
// Reference the `name`.
payload.name = args[0];
// Reference the `dependencies`.
payload.deps = args[1];
// Reference the `factory`.
payload.factory = args[2];
// Return our factored payload.
return payload;
};
// Fill up our dependencies based on our arguments if we need to.
define.deps = function(args) {
// If we already have our dependencies defined, then skip the function.
if (args.deps) {
return args;
}
// If our factory isn't a function, and our dependcies are empty, then we have no dependencies.
if (!me.isFunction(args.factory)) {
args.deps = [];
return args;
}
// If we made it this far, set the arguments from our factory as our dependencies.
args.deps = me.args(args.factory);
// Send back our arguments array with our populated dependencies.
return args;
};
// Generate and return our new module.
define.module = function(args) {
// Generate or reference the module.
var module = me.module(args.name, {
amd: true
});
// Set our dependencies.
module.deps = me.normalizeStringSeries(args.deps);
// Set our error.
module.error = me.normalizeFunction(args.error);
// Set our factory.
module.factory = args.factory;
// Explicity flag that the module has been loaded, that way when we reference it, we don't attempt to load it.
module.loader.loaded = true;
// Return a reference to the module.
return module;
};
// Our last defined module reference. This is used to reference the proper names with our anonymous modules.
define.module.last = null;
// Reference the module within the library.
me.define = define;