/*global module*/ var Injector = (function() { var Injector = function() { this.specs = {}; this.values = {}; this.stack = []; this.usedRequestor = null; }; Injector.prototype.register = function(name, spec) { if (typeof(name) == "function" || name instanceof Function) { this.specs[name.name] = name; } else if (typeof(name) == "string" || name instanceof String) { this.specs[name] = spec; } else { for (var key in name) { this.register(key, name[key]); } } return this; }; Injector.prototype.get = function(name) { if (name in this.specs) { var oldUsedRequestor = this.usedRequestor; this.usedRequestor = false; this.stack.push(name); try { throwIfCyclic(name, this.stack); var result = this.invoke(this.specs[name]); if (this.usedRequestor) { return result; } else { delete this.specs[name]; return (this.values[name] = result); } } catch (e) { throw (e instanceof InjectorError ? e : new InjectorError("Error constructing value for " + stackString(this.stack), e)); } finally { this.stack.pop(); this.usedRequestor = oldUsedRequestor; } } if (name in this.values) { return this.values[name]; } else { throw new InjectorError("Dependency " + name + " not found"); } }; Injector.prototype.requestor = function() { switch (this.stack.length) { case 0: throw new InjectorError("Cannot use requestor for invoked function - none exists"); case 1: throw new InjectorError("Cannot use requestor for top-level constructor - none exists"); default: if (this.usedRequestor === false) this.usedRequestor = true; return this.stack[this.stack.length - 2]; } }; Injector.prototype.invoke = function(spec) { var parsed = parseSpec(spec); var fn = parsed[0]; var dependencies = parsed[1]; return fn.apply(this, dependencies.map(function(dependency) { return this.get(dependency); }, this)); }; Injector.prototype.construct = function() { }; var parsingRegex = /^function[^(]*\(([^)]*)\)/; var parseFnArgs = function(fn) { var parts = parsingRegex.exec(fn.toString().replace(/\s+/g, "")); if (parts == null) { throw new Error("Unable to parse fn definition"); } else { return parts[1] ? parts[1].split(/,/) : []; } }; var parseSpec = function(spec) { var fn, dependencies; if (typeof(spec) == "function" || spec instanceof Function) { dependencies = parseFnArgs(spec); fn = spec; } else { fn = spec[spec.length - 1]; dependencies = spec.slice(0, spec.length - 1); } return [fn, dependencies]; }; var stackString = function(stack) { stack.reverse(); var result = stack.join(" <- "); stack.reverse(); return result; }; var throwIfCyclic = function(name, stack) { if (stack.indexOf(name) != stack.length - 1) { throw new InjectorError("Cyclic dependency detected " + stackString(stack)); } }; var InjectorError = function(message, cause) { this.name = "InjectorError"; this.message = message; this.cause = cause; }; InjectorError.prototype = new Error(); InjectorError.prototype.constructor = InjectorError; InjectorError.prototype.toString = function() { return "InjectorError: " + this.message + (this.cause ? " [caused by " + this.cause + "]" : ""); }; return Injector; })(); if (typeof(module) !== "undefined") module.exports = Injector;