Created
November 21, 2010 20:02
-
-
Save kixxauth/709088 to your computer and use it in GitHub Desktop.
Enterprice Resource Management application with CommonJS 2.0 JS loader.
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
// ======================== | |
// CommonJS 2.0 environment | |
// ======================== | |
// | |
// (unratified draft specification) | |
// -------------------------------- | |
var env = (function (GLOBAL, undefined) { | |
'use strict'; | |
// memoized exports objects | |
var require_memo = {} | |
// memoized factory functions | |
, factory_memo = {} | |
// memoized module objects | |
, module_memo = {} | |
// the currently loading module id | |
, currently_loading | |
// memoized module resources (as functions) | |
, resource_cache = {} | |
// just a shortcut | |
, hasown = Object.prototype.hasOwnProperty | |
// constructors defined later | |
, require | |
, module | |
; | |
// custom error constructor | |
function requireError(self) { | |
self.name = 'RequireError'; | |
return self; | |
} | |
// cache a resource function | |
function resource(path, fn) { | |
resource_cache[path] = fn; | |
} | |
// dereference an exports object from the memo and return it | |
function get(id) { | |
if (hasown.call(require_memo, id)) { | |
return require_memo[id]; | |
} | |
throw requireError( | |
new Error('The module "'+ id +'" could not be found.')); | |
} | |
// create and memoize a new exports object with the given id | |
function set(id) { | |
if (!hasown.call(require_memo, id)) { | |
return (require_memo[id] = {}); | |
} | |
throw requireError( | |
new Error('The module "'+ id +'" already exists.')); | |
} | |
// determine if a module factory has already been loaded | |
function loaded(id) { | |
return hasown.call(factory_memo, id); | |
} | |
// invoke a module resource | |
function load(path) { | |
currently_loading = path; | |
resource_cache[path](GLOBAL); | |
} | |
// determine the currently loading module id | |
function loading() { | |
return currently_loading; | |
} | |
// get or set a module factory function | |
function factories(key, fn) { | |
if (fn) { | |
factory_memo[key] = fn; | |
return; | |
} | |
return factory_memo[key]; | |
} | |
// determine if a factory function has already been invoked | |
function invoked(id) { | |
return hasown.call(module_memo, id); | |
} | |
// invoke a factory function with the given id and exports object | |
function doinvoke(id, exports) { | |
var require = requireFactory(id) | |
, mod = (module_memo[id] = module({id:id, require: require})) | |
; | |
factories(id)(require, exports, mod); | |
} | |
// return a resolved id given the current location and a relative id | |
function resolve(current, id) { | |
// A module id that does not begin with a '.' is an absolute | |
// identifier, so we just return it. | |
if (id.charAt(0) !== '.') { | |
return id; | |
} | |
var resolved = current.split('/') | |
, parts = id.split('/') | |
, i | |
, part | |
; | |
// Pop off the module name of the current location. | |
resolved.pop(); | |
for (i = 0; i < parts.length; i += 1) { | |
part = parts[i]; | |
if (part.charAt(0) !== '.') { | |
resolved.push(part); | |
} | |
else if (part === '..') { | |
resolved.pop(); | |
} | |
} | |
return resolved.join('/'); | |
} | |
// constructor | |
module = function Module(spec) { | |
if (!(this instanceof Module)) { | |
return new Module(spec); | |
} | |
this.id = spec.id; | |
this.require = spec.require; | |
}; | |
// declare / define a module and dependencies | |
Module.prototype.declare = function (deps, factory) { | |
if (typeof deps === 'function') { | |
factory = deps; | |
deps = []; | |
} | |
var id = loading(); | |
this.require.memoize(id); | |
if (deps.length) { | |
this.provide(deps, function () { | |
factories(id, factory); | |
}); | |
return; | |
} | |
factories(id, factory); | |
}; | |
// provide module dependencies to the environment | |
Module.prototype.provide = function (deps, callback) { | |
var resolver = this.require.id | |
, i | |
, id | |
; | |
for (i = 0; i < deps.length; i += 1) { | |
id = resolver(deps[i]); | |
if (!loaded(id)) { | |
load(id); | |
} | |
} | |
callback(); | |
}; | |
// not implemented | |
Module.prototype.load = null; | |
// contructor | |
// the 'extra module environment' should specify `extra_require` | |
requireFactory = function Require(loc, extra_require) { | |
var self; | |
// the actual `require` function | |
function new_require(id) { | |
id = self.id(id); | |
var module_exports = get(id); | |
if (!invoked(id)) { | |
doinvoke(id, module_exports); | |
} | |
return module_exports; | |
} | |
// use a different require function if specified | |
self = typeof extra_require === 'function' ? | |
extra_require : new_require; | |
// resolve an id | |
self.id = function (id) { | |
return resolve(loc, id); | |
}; | |
// creat and memoize and exports object with the given id | |
self.memoize = set; | |
return self; | |
}; | |
// special require function for the 'extra module environment' | |
function extra_require(id) { | |
if (id.charAt(0) !== '.') { | |
var module_exports = get(id); | |
doinvoke(id, module_exports); | |
return module_exports; | |
} | |
throw requireError(new Error( 'Relative module identifiers are not '+ | |
'available in the extra module '+ | |
'environment: '+ id)); | |
} | |
// export the globals | |
GLOBAL.require = requireFactory('', extra_require); | |
GLOBAL.module = module({require: GLOBAL.require}); | |
// special module.declare function used to bootstrap the main module | |
GLOBAL.module.declare = function bootstrap(deps, factory) { | |
var id = ''; | |
// remove the declare instance method | |
delete this.declare; // the prototype.declare method remains | |
// define the resource containing the main module | |
resource(id, function () { this.declare(deps, factory); }); | |
load(id); | |
this.require(id); | |
}; | |
// return the `env` namespace | |
return {resource: resource}; | |
}(window)); | |
// ============================= | |
// CommonJS 2.0 compient modules | |
// ============================= | |
// ---------------------------------------------------------------------------- | |
env.resource('thrower', function (window) { | |
var sto = window.setTimeout; | |
/** | |
* @module thrower | |
* @exports {Function} thrower | |
*/ | |
module.declare(function (require, exports, module) { | |
'use strict'; | |
/** | |
* Delay throwing an error by putting it on top of the execution stack. | |
* @param {Object} e Something to throw; usually an `Error` object. | |
*/ | |
exports.thrower = function thrower(e) { | |
sto(function () { throw e; }, 0); | |
}; | |
}); }); | |
// ---------------------------------------------------------------------------- | |
env.resource('broadcaster', function (window) { | |
var deps = ['thrower']; | |
/** | |
* @module broadcaster | |
* @exports {Function} broadcaster | |
*/ | |
module.declare(deps, function (require, exports, module) { | |
'use strict'; | |
var thrower = require('thrower').thrower; | |
/** | |
* Broadcast a set of arguments to a list of functions. | |
* @param {Array} callbacks A list of functions to broadcast to. | |
* @param {Array} args A list of arguments to apply to each callback. | |
* @param {Object} [context] Will become `this` inside callback functions. | |
*/ | |
exports.broadcast = function broadcastor(callbacks, args, context) { | |
var i = 0, len = callbacks.length; | |
context = context || {}; | |
for (; i < len; i += 1) { | |
try { | |
callbacks[i].apply(context, args); | |
} catch (e) { | |
// Report a callback error after it can no longer get in our way. | |
thrower(e); | |
} | |
} | |
}; | |
}); }); | |
// ---------------------------------------------------------------------------- | |
env.resource('namespaced-emitter', function (window) { | |
var deps = ['broadcaster']; | |
/** | |
* @module | |
*/ | |
module.declare(deps, function (require, exports, module) { | |
'use strict'; | |
var broadcast = require('broadcaster').broadcast | |
, hasown = Object.prototype.hasOwnProperty | |
; | |
/** | |
* Bind emitter methods to an object. | |
* @param {Object} [self] The object to bind to. Defaults to `{}`. | |
* @returns {Object} The same object passed in; or `{}`. | |
* | |
* The `.emit()` and `.on()` methods are bound to the object. | |
*/ | |
exports.emitter = function emitter(self) { | |
self = self || {}; | |
var registry = []; | |
/** | |
* Emit event data to registered handlers. | |
* @param {String} path Namespaced event name. | |
* @param {Array} data Array of arguments to pass to handlers. | |
*/ | |
self.emit = function emit(path, data) { | |
while (path) { | |
if (hasown.call(registry, path)) { | |
broadcast(registry[path], data); | |
} | |
path = path.slice(0, path.lastIndexOf('.')); | |
} | |
}; | |
/** | |
* Bind an event handler to this emitter object. | |
* @param {String} path Namespaced event name. | |
* @param {Function} fn The callback function to call. | |
* This will most likely be your public API. | |
*/ | |
self.on = function on(path, fn) { | |
if (!hasown.call(registry, path)) { | |
registry[path] = []; | |
} | |
registry[path].push(fn); | |
}; | |
return self; | |
}; | |
}); }); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment